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