1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
6 #define IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
7 
8 #include "ios/web/public/js_messaging/web_frame.h"
9 
10 #include <map>
11 #include <string>
12 
13 #include "base/cancelable_callback.h"
14 #include "base/macros.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/values.h"
17 #include "crypto/symmetric_key.h"
18 #import "ios/web/public/web_state.h"
19 #include "ios/web/public/web_state_observer.h"
20 #include "url/gurl.h"
21 
22 namespace web {
23 
24 class WebFrameImpl : public WebFrame, public web::WebStateObserver {
25  public:
26   // Creates a new WebFrame. |initial_message_id| will be used as the message ID
27   // of the next message sent to the frame with the |CallJavaScriptFunction|
28   // API.
29   WebFrameImpl(const std::string& frame_id,
30                bool is_main_frame,
31                GURL security_origin,
32                web::WebState* web_state);
33   ~WebFrameImpl() override;
34 
35   // Sets the value to use for the next message ID.
36   void SetNextMessageId(int message_id);
37   // Sets the key to use for message encryption.
38   void SetEncryptionKey(std::unique_ptr<crypto::SymmetricKey> frame_key);
39   // The associated web state.
40   WebState* GetWebState();
41 
42   // WebFrame implementation
43   std::string GetFrameId() const override;
44   bool IsMainFrame() const override;
45   GURL GetSecurityOrigin() const override;
46   bool CanCallJavaScriptFunction() const override;
47 
48   bool CallJavaScriptFunction(
49       const std::string& name,
50       const std::vector<base::Value>& parameters) override;
51   bool CallJavaScriptFunction(
52       const std::string& name,
53       const std::vector<base::Value>& parameters,
54       base::OnceCallback<void(const base::Value*)> callback,
55       base::TimeDelta timeout) override;
56 
57   // WebStateObserver implementation
58   void WebStateDestroyed(web::WebState* web_state) override;
59 
60  private:
61   // Calls the JavaScript function |name| in the frame context in the same
62   // manner as the inherited CallJavaScriptFunction functions. If
63   // |reply_with_result| is true, the return value of executing the function
64   // will be sent back to the receiver and handled by |OnJavaScriptReply|.
65   bool CallJavaScriptFunction(const std::string& name,
66                               const std::vector<base::Value>& parameters,
67                               bool reply_with_result);
68 
69   // Detaches the receiver from the associated  WebState.
70   void DetachFromWebState();
71   // Returns the script command name to use for this WebFrame.
72   const std::string GetScriptCommandPrefix();
73   // Encrypts |payload| and returns a JSON string of a dictionary containing
74   // the encrypted metadata and its initialization vector. If encryption fails,
75   // an empty string will be returned.
76   const std::string EncryptPayload(base::DictionaryValue payload,
77                                    const std::string& additiona_data);
78 
79   // A structure to store the callbacks associated with the
80   // |CallJavaScriptFunction| requests.
81   typedef base::CancelableOnceCallback<void(void)> TimeoutCallback;
82   struct RequestCallbacks {
83     RequestCallbacks(base::OnceCallback<void(const base::Value*)> completion,
84                      std::unique_ptr<TimeoutCallback>);
85     ~RequestCallbacks();
86     base::OnceCallback<void(const base::Value*)> completion;
87     std::unique_ptr<TimeoutCallback> timeout_callback;
88   };
89 
90   // Calls the JavaScript function |name| in the web state (main frame). If
91   // |reply_with_result| is true, the return value of executing the function
92   // will be sent back to the receiver. This function is only used if the
93   // receiver does not have an encryption key. The JavaScript function is called
94   // directly and thus only works on the main frame. (Encryption is not required
95   // to securely communicate with the main frame because evaluating JavaScript
96   // on the WebState is already secure.)
97   bool ExecuteJavaScriptFunction(const std::string& name,
98                                  const std::vector<base::Value>& parameters,
99                                  int message_id,
100                                  bool reply_with_result);
101 
102   // Runs the request associated with the message with id |message_id|. The
103   // completion callback, if any, associated with |message_id| will be called
104   // with |result|.
105   void CompleteRequest(int message_id, const base::Value* result);
106   // Calls the completion block of |request_callbacks| with |result| value and
107   // removes the callbacks from |pending_requests|.
108   void CompleteRequest(std::unique_ptr<RequestCallbacks> request_callbacks,
109                        const base::Value* result);
110 
111   // Cancels the request associated with the message with id |message_id|. The
112   // completion callback, if any, associated with |message_id| will be called
113   // with a null result value. Note that the JavaScript will still run to
114   // completion, but any future response will be ignored.
115   void CancelRequest(int message_id);
116   // Performs |CancelRequest| on all outstanding request callbacks in
117   // |pending_requests_|.
118   void CancelPendingRequests();
119 
120   // Handles message from JavaScript with result of executing the function
121   // specified in CallJavaScriptFunction.
122   void OnJavaScriptReply(web::WebState* web_state,
123                          const base::DictionaryValue& command,
124                          const GURL& page_url,
125                          bool interacting,
126                          WebFrame* sender_frame);
127 
128   // The JavaScript requests awating a reply.
129   std::map<uint32_t, std::unique_ptr<struct RequestCallbacks>>
130       pending_requests_;
131 
132   // The frame identifier which uniquely identifies this frame across the
133   // application's lifetime.
134   std::string frame_id_;
135   // The symmetric encryption key used to encrypt messages addressed to the
136   // frame. Stored in a base64 encoded string.
137   std::unique_ptr<crypto::SymmetricKey> frame_key_;
138   // The message ID of the next JavaScript message to be sent.
139   int next_message_id_ = 0;
140   // Whether or not the receiver represents the main frame.
141   bool is_main_frame_ = false;
142   // The security origin associated with this frame.
143   GURL security_origin_;
144   // The associated web state.
145   web::WebState* web_state_ = nullptr;
146   // Subscription for JS message.
147   std::unique_ptr<web::WebState::ScriptCommandSubscription> subscription_;
148 
149   base::WeakPtrFactory<WebFrameImpl> weak_ptr_factory_;
150 
151   DISALLOW_COPY_AND_ASSIGN(WebFrameImpl);
152 };
153 
154 }  // namespace web
155 
156 #endif  // IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
157