1 // Copyright 2017 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 CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
6 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
7 
8 #include <string>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
14 #include "base/macros.h"
15 #include "chrome/browser/chromeos/login/oobe_screen.h"
16 #include "chrome/browser/ui/webui/chromeos/login/js_calls_container.h"
17 #include "components/login/base_screen_handler_utils.h"
18 #include "content/public/browser/web_ui_message_handler.h"
19 #include "ui/gfx/native_widget_types.h"
20 
21 namespace base {
22 class DictionaryValue;
23 class ListValue;
24 }  // namespace base
25 
26 namespace login {
27 class LocalizedValuesBuilder;
28 }
29 
30 namespace chromeos {
31 
32 class OobeUI;
33 
34 // Base class for all oobe/login WebUI handlers. These handlers are the binding
35 // layer that allow the C++ and JavaScript code to communicate.
36 //
37 // If the deriving type is associated with a specific OobeScreen, it should
38 // derive from BaseScreenHandler instead of BaseWebUIHandler.
39 class BaseWebUIHandler : public content::WebUIMessageHandler {
40  public:
41   explicit BaseWebUIHandler(JSCallsContainer* js_calls_container);
42   ~BaseWebUIHandler() override;
43 
44   // Gets localized strings to be used on the page.
45   void GetLocalizedStrings(base::DictionaryValue* localized_strings);
46 
47   // WebUIMessageHandler implementation:
48   void RegisterMessages() override;
49 
50   // This method is called when page is ready. It propagates to inherited class
51   // via virtual Initialize() method (see below).
52   void InitializeBase();
53 
54  protected:
55   // All subclasses should implement this method to provide localized values.
56   virtual void DeclareLocalizedValues(
57       ::login::LocalizedValuesBuilder* builder) = 0;
58 
59   // All subclasses should implement this method to register callbacks for JS
60   // messages.
DeclareJSCallbacks()61   virtual void DeclareJSCallbacks() {}
62 
63   // Subclasses can override these methods to pass additional parameters
64   // to loadTimeData.
65   virtual void GetAdditionalParameters(base::DictionaryValue* parameters);
66 
67   // Run a JavaScript function. If the backing webui that this handler is not
68   // fully loaded, then the JS call will be deferred and executed after the
69   // initialize message.
70   //
71   // All CallJS invocations can be recorded for tests if CallJS recording is
72   // enabled.
73   template <typename... Args>
CallJS(const std::string & function_name,const Args &...args)74   void CallJS(const std::string& function_name, const Args&... args) {
75     // Record the call if the WebUI is not loaded or if we are in a test.
76     if (!js_calls_container_->is_initialized() ||
77         js_calls_container_->record_all_events_for_test()) {
78       std::vector<base::Value> arguments;
79       InsertIntoList(&arguments, args...);
80       js_calls_container_->events()->emplace_back(
81           JSCallsContainer::Event(JSCallsContainer::Event::Type::kOutgoing,
82                                   function_name, std::move(arguments)));
83     }
84 
85     // Make the call now if the WebUI is loaded.
86     if (js_calls_container_->is_initialized())
87       web_ui()->CallJavascriptFunctionUnsafe(
88           function_name, ::login::MakeValue(args).Clone()...);
89   }
90 
91   // Register WebUI callbacks. The callbacks will be recorded if recording is
92   // enabled.
93   template <typename T>
AddRawCallback(const std::string & function_name,void (T::* method)(const base::ListValue * args))94   void AddRawCallback(const std::string& function_name,
95                       void (T::*method)(const base::ListValue* args)) {
96     content::WebUI::MessageCallback callback =
97         base::BindRepeating(method, base::Unretained(static_cast<T*>(this)));
98     web_ui()->RegisterMessageCallback(
99         function_name,
100         base::BindRepeating(&BaseWebUIHandler::OnRawCallback,
101                             base::Unretained(this), function_name, callback));
102   }
103   template <typename T, typename... Args>
AddCallback(const std::string & function_name,void (T::* method)(Args...))104   void AddCallback(const std::string& function_name,
105                    void (T::*method)(Args...)) {
106     base::RepeatingCallback<void(Args...)> callback =
107         base::BindRepeating(method, base::Unretained(static_cast<T*>(this)));
108     web_ui()->RegisterMessageCallback(
109         function_name,
110         base::BindRepeating(&BaseWebUIHandler::OnCallback<Args...>,
111                             base::Unretained(this), function_name, callback));
112   }
113 
114   // Called when the page is ready and handler can do initialization.
115   virtual void Initialize() = 0;
116 
117   // Show selected WebUI `screen`.
118   void ShowScreen(OobeScreenId screen);
119   // Show selected WebUI `screen`. Pass screen initialization using the `data`
120   // parameter.
121   void ShowScreenWithData(OobeScreenId screen,
122                           const base::DictionaryValue* data);
123 
124   // Returns the OobeUI instance.
125   OobeUI* GetOobeUI() const;
126 
127   // Returns current visible OOBE screen.
128   OobeScreenId GetCurrentScreen() const;
129 
130   // Whether page is ready.
page_is_ready()131   bool page_is_ready() const { return page_is_ready_; }
132 
133  private:
134   friend class OobeUI;
135 
136   // InsertIntoList takes a template parameter pack and expands into the
137   // following form:
138   //
139   //   for (auto arg : args)
140   //     stroage->emplace_back(::login::MakeValue(arg).Clone());
141   //
142   // This cannot be expressed with the current parameter pack expansion rules
143   // and the only way to do it is via compile-time recursion.
144   template <typename Head, typename... Tail>
InsertIntoList(std::vector<base::Value> * storage,const Head & head,const Tail &...tail)145   void InsertIntoList(std::vector<base::Value>* storage,
146                       const Head& head,
147                       const Tail&... tail) {
148     storage->emplace_back(::login::MakeValue(head).Clone());
149     InsertIntoList(storage, tail...);
150   }
151   // Base condition for the recursion, when there are no more elements to
152   // insert. Does nothing.
153   void InsertIntoList(std::vector<base::Value>*);
154 
155   // Record `function_name` and `args` as an incoming event if recording is
156   // enabled.
157   void MaybeRecordIncomingEvent(const std::string& function_name,
158                                 const base::ListValue* args);
159 
160   // These two functions wrap Add(Raw)Callback so that the incoming JavaScript
161   // event can be recorded.
162   void OnRawCallback(const std::string& function_name,
163                      const content::WebUI::MessageCallback callback,
164                      const base::ListValue* args);
165   template <typename... Args>
OnCallback(const std::string & function_name,const base::RepeatingCallback<void (Args...)> & callback,const base::ListValue * args)166   void OnCallback(const std::string& function_name,
167                   const base::RepeatingCallback<void(Args...)>& callback,
168                   const base::ListValue* args) {
169     MaybeRecordIncomingEvent(function_name, args);
170     ::login::CallbackWrapper<Args...>(callback, args);
171   }
172 
173   // Keeps whether page is ready.
174   bool page_is_ready_ = false;
175 
176   JSCallsContainer* js_calls_container_ = nullptr;  // non-owning pointers.
177 
178   DISALLOW_COPY_AND_ASSIGN(BaseWebUIHandler);
179 };
180 
181 }  // namespace chromeos
182 
183 #endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
184