1 // Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights 2 // reserved. Use of this source code is governed by a BSD-style license that 3 // can be found in the LICENSE file. 4 5 #include "tests/ceftests/extensions/extension_test_handler.h" 6 #include "tests/ceftests/test_util.h" 7 #include "tests/shared/browser/extension_util.h" 8 9 #define ALARMS_TEST_GROUP_ALL(name, test_class) \ 10 EXTENSION_TEST_GROUP_ALL(ChromeAlarms##name, test_class) 11 #define ALARMS_TEST_GROUP_MINIMAL(name, test_class) \ 12 EXTENSION_TEST_GROUP_MINIMAL(ChromeAlarms##name, test_class) 13 14 namespace { 15 16 const char kExtensionPath[] = "alarms-extension"; 17 const char kSuccessMessage[] = "success"; 18 19 // Base class for testing chrome.alarms methods. 20 // See https://developer.chrome.com/extensions/alarms 21 class AlarmsTestHandler : public ExtensionTestHandler { 22 public: AlarmsTestHandler(RequestContextType request_context_type)23 explicit AlarmsTestHandler(RequestContextType request_context_type) 24 : ExtensionTestHandler(request_context_type) { 25 // Only creating the extension browser. 26 set_create_main_browser(false); 27 } 28 29 // CefExtensionHandler methods: OnExtensionLoaded(CefRefPtr<CefExtension> extension)30 void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override { 31 EXPECT_TRUE(CefCurrentlyOn(TID_UI)); 32 EXPECT_FALSE(got_loaded_); 33 got_loaded_.yes(); 34 35 // Verify |extension| contents. 36 EXPECT_FALSE(extension->GetIdentifier().empty()); 37 EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(), 38 client::extension_util::GetInternalExtensionResourcePath( 39 extension->GetPath()) 40 .c_str()); 41 TestDictionaryEqual(CreateManifest(), extension->GetManifest()); 42 43 EXPECT_FALSE(extension_); 44 extension_ = extension; 45 46 CreateBrowserForExtension(); 47 } 48 OnExtensionUnloaded(CefRefPtr<CefExtension> extension)49 void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override { 50 EXPECT_TRUE(CefCurrentlyOn(TID_UI)); 51 EXPECT_TRUE(extension_); 52 EXPECT_TRUE(extension_->IsSame(extension)); 53 EXPECT_FALSE(got_unloaded_); 54 got_unloaded_.yes(); 55 extension_ = nullptr; 56 57 // Execute asynchronously so call stacks have a chance to unwind. 58 // Will close the browser windows. 59 CefPostTask(TID_UI, base::BindOnce(&AlarmsTestHandler::DestroyTest, this)); 60 } 61 62 // CefLoadHandler methods: OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)63 void OnLoadingStateChange(CefRefPtr<CefBrowser> browser, 64 bool isLoading, 65 bool canGoBack, 66 bool canGoForward) override { 67 CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension(); 68 EXPECT_TRUE(extension); 69 EXPECT_TRUE(extension_->IsSame(extension)); 70 71 if (isLoading) { 72 EXPECT_FALSE(extension_browser_); 73 extension_browser_ = browser; 74 } else { 75 EXPECT_TRUE(browser->IsSame(extension_browser_)); 76 77 const std::string& url = browser->GetMainFrame()->GetURL(); 78 EXPECT_STREQ(extension_url_.c_str(), url.c_str()); 79 } 80 } 81 82 // CefResourceRequestHandler methods: GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)83 CefRefPtr<CefResourceHandler> GetResourceHandler( 84 CefRefPtr<CefBrowser> browser, 85 CefRefPtr<CefFrame> frame, 86 CefRefPtr<CefRequest> request) override { 87 EXPECT_TRUE(browser->IsSame(extension_browser_)); 88 89 CefRefPtr<CefExtension> extension = browser->GetHost()->GetExtension(); 90 EXPECT_TRUE(extension); 91 EXPECT_TRUE(extension_->IsSame(extension)); 92 93 const std::string& url = request->GetURL(); 94 EXPECT_STREQ(extension_url_.c_str(), url.c_str()); 95 96 EXPECT_FALSE(got_url_request_); 97 got_url_request_.yes(); 98 99 // Handle the resource request. 100 return RoutingTestHandler::GetResourceHandler(browser, frame, request); 101 } 102 103 protected: OnLoadExtensions()104 void OnLoadExtensions() override { 105 LoadExtension(kExtensionPath, CreateManifest()); 106 } 107 OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)108 bool OnMessage(CefRefPtr<CefBrowser> browser, 109 const std::string& message) override { 110 if (message == "extension_onload") { 111 // From body onLoad in the extension browser. 112 EXPECT_TRUE(browser->IsSame(extension_browser_)); 113 EXPECT_FALSE(got_body_onload_); 114 got_body_onload_.yes(); 115 TriggerAlarmsApiJSFunction(); 116 return true; 117 } 118 EXPECT_TRUE(browser->IsSame(extension_browser_)); 119 EXPECT_FALSE(got_success_message_); 120 got_success_message_.yes(); 121 EXPECT_STREQ(kSuccessMessage, message.c_str()); 122 TriggerDestroyTest(); 123 return true; 124 } 125 OnDestroyTest()126 void OnDestroyTest() override { 127 extension_browser_ = nullptr; 128 129 EXPECT_TRUE(got_loaded_); 130 EXPECT_TRUE(got_url_request_); 131 EXPECT_TRUE(got_body_onload_); 132 EXPECT_TRUE(got_trigger_api_function_); 133 EXPECT_TRUE(got_success_message_); 134 EXPECT_TRUE(got_unloaded_); 135 } 136 137 // Create a manifest that grants access to the alarms API. CreateManifest() const138 virtual CefRefPtr<CefDictionaryValue> CreateManifest() const { 139 ApiPermissionsList api_permissions; 140 api_permissions.push_back("alarms"); 141 return CreateDefaultManifest(api_permissions); 142 } 143 144 // Add resources in the extension browser. OnAddExtensionResources(const std::string & origin)145 virtual void OnAddExtensionResources(const std::string& origin) { 146 extension_url_ = origin + "extension.html"; 147 AddResource(extension_url_, GetExtensionHTML(), "text/html"); 148 } 149 150 // Returns the chrome.alarms.* JS that is executed in the extension browser 151 // when the triggerAlarmsApi() JS function is called. 152 virtual std::string GetAlarmsApiJS() const = 0; 153 154 // Returns the JS that will be loaded in the extension browser. This 155 // implements the triggerAlarmsApi() JS function called from 156 // TriggerAlarmsApiJSFunction(). GetExtensionJS() const157 virtual std::string GetExtensionJS() const { 158 return "function triggerAlarmsApi() {" + GetAlarmsApiJS() + "}"; 159 } 160 161 // Returns the HTML that will be loaded in the extension browser. GetExtensionHTML() const162 virtual std::string GetExtensionHTML() const { 163 return "<html><head><script>" + GetExtensionJS() + 164 "</script></head><body onLoad=" + GetMessageJS("extension_onload") + 165 ">Extension</body></html>"; 166 } 167 TriggerDestroyTest()168 virtual void TriggerDestroyTest() { 169 // Execute asynchronously so call stacks have a chance to unwind. 170 CefPostTask(TID_UI, base::BindOnce(&AlarmsTestHandler::UnloadExtension, 171 this, extension_)); 172 } 173 extension() const174 CefRefPtr<CefExtension> extension() const { return extension_; } extension_url() const175 std::string extension_url() const { return extension_url_; } extension_browser() const176 CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; } 177 got_success_message() const178 bool got_success_message() const { return got_success_message_; } 179 180 private: CreateBrowserForExtension()181 void CreateBrowserForExtension() { 182 const std::string& identifier = extension_->GetIdentifier(); 183 EXPECT_FALSE(identifier.empty()); 184 const std::string& origin = 185 client::extension_util::GetExtensionOrigin(identifier); 186 EXPECT_FALSE(origin.empty()); 187 188 // Add extension resources. 189 OnAddExtensionResources(origin); 190 191 // Create a browser to host the extension. 192 CreateBrowser(extension_url_, request_context()); 193 } 194 TriggerAlarmsApiJSFunction()195 void TriggerAlarmsApiJSFunction() { 196 EXPECT_FALSE(got_trigger_api_function_); 197 got_trigger_api_function_.yes(); 198 199 extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerAlarmsApi();", 200 extension_url_, 0); 201 } 202 203 CefRefPtr<CefExtension> extension_; 204 std::string extension_url_; 205 CefRefPtr<CefBrowser> extension_browser_; 206 207 TrackCallback got_loaded_; 208 TrackCallback got_url_request_; 209 TrackCallback got_body_onload_; 210 TrackCallback got_trigger_api_function_; 211 TrackCallback got_success_message_; 212 TrackCallback got_unloaded_; 213 }; 214 } // namespace 215 216 namespace { 217 218 // Test for chrome.alarms.create(string name, object alarmInfo) 219 // and chrome.alarms.onAlarm.addListener(function callback) 220 class CreateAlarmTestHandler : public AlarmsTestHandler { 221 public: CreateAlarmTestHandler(RequestContextType request_context_type)222 explicit CreateAlarmTestHandler(RequestContextType request_context_type) 223 : AlarmsTestHandler(request_context_type) {} 224 225 protected: GetAlarmsApiJS() const226 std::string GetAlarmsApiJS() const override { 227 return "chrome.alarms.onAlarm.addListener(function (alarm) {" + 228 GetMessageJS(kSuccessMessage) + 229 "});" 230 "chrome.alarms.create(\"test\", {delayInMinutes:0.01})"; 231 } 232 233 private: 234 IMPLEMENT_REFCOUNTING(CreateAlarmTestHandler); 235 DISALLOW_COPY_AND_ASSIGN(CreateAlarmTestHandler); 236 }; 237 } // namespace 238 239 ALARMS_TEST_GROUP_ALL(CreateAlarm, CreateAlarmTestHandler) 240 241 namespace { 242 243 // Test for chrome.alarms.get(string name, function callback) 244 class GetAlarmTestHandler : public AlarmsTestHandler { 245 public: GetAlarmTestHandler(RequestContextType request_context_type)246 explicit GetAlarmTestHandler(RequestContextType request_context_type) 247 : AlarmsTestHandler(request_context_type) {} 248 249 protected: GetAlarmsApiJS() const250 std::string GetAlarmsApiJS() const override { 251 return "chrome.alarms.create(\"test\", {delayInMinutes:1});" 252 "setTimeout(function() {" 253 "chrome.alarms.get(\"test\", function (alarm) {" + 254 GetMessageJS(kSuccessMessage) + "})}, 100)"; 255 } 256 257 private: 258 IMPLEMENT_REFCOUNTING(GetAlarmTestHandler); 259 DISALLOW_COPY_AND_ASSIGN(GetAlarmTestHandler); 260 }; 261 } // namespace 262 263 ALARMS_TEST_GROUP_MINIMAL(GetAlarm, GetAlarmTestHandler) 264 265 namespace { 266 267 // Test for chrome.alarms.getAll(function callback) 268 class GetAllAlarmsTestHandler : public AlarmsTestHandler { 269 public: GetAllAlarmsTestHandler(RequestContextType request_context_type)270 explicit GetAllAlarmsTestHandler(RequestContextType request_context_type) 271 : AlarmsTestHandler(request_context_type) {} 272 273 protected: GetAlarmsApiJS() const274 std::string GetAlarmsApiJS() const override { 275 return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});" 276 "chrome.alarms.create(\"alarm2\", {delayInMinutes:1});" 277 "setTimeout(function() {" 278 "chrome.alarms.getAll(function (alarms) {" 279 "if (alarms.length == 2) {" + 280 GetMessageJS(kSuccessMessage) + "}})}, 100)"; 281 } 282 283 private: 284 IMPLEMENT_REFCOUNTING(GetAllAlarmsTestHandler); 285 DISALLOW_COPY_AND_ASSIGN(GetAllAlarmsTestHandler); 286 }; 287 } // namespace 288 289 ALARMS_TEST_GROUP_MINIMAL(GetAllAlarms, GetAllAlarmsTestHandler) 290 291 namespace { 292 293 // Test for chrome.alarms.clear(string name, function callback) 294 class ClearAlarmTestHandler : public AlarmsTestHandler { 295 public: ClearAlarmTestHandler(RequestContextType request_context_type)296 explicit ClearAlarmTestHandler(RequestContextType request_context_type) 297 : AlarmsTestHandler(request_context_type) {} 298 299 protected: GetAlarmsApiJS() const300 std::string GetAlarmsApiJS() const override { 301 return "chrome.alarms.create(\"test\", {delayInMinutes:1});" 302 "setTimeout(function() {" 303 "chrome.alarms.clear(\"test\", function (wasCleared) {" 304 "if (wasCleared) {" + 305 GetMessageJS(kSuccessMessage) + "}})}, 100)"; 306 } 307 308 private: 309 IMPLEMENT_REFCOUNTING(ClearAlarmTestHandler); 310 DISALLOW_COPY_AND_ASSIGN(ClearAlarmTestHandler); 311 }; 312 } // namespace 313 314 ALARMS_TEST_GROUP_MINIMAL(ClearAlarm, ClearAlarmTestHandler) 315 316 namespace { 317 318 // Test for chrome.alarms.clearAll(function callback) 319 class ClearAllAlarmsTestHandler : public AlarmsTestHandler { 320 public: ClearAllAlarmsTestHandler(RequestContextType request_context_type)321 explicit ClearAllAlarmsTestHandler(RequestContextType request_context_type) 322 : AlarmsTestHandler(request_context_type) {} 323 324 protected: GetAlarmsApiJS() const325 std::string GetAlarmsApiJS() const override { 326 return "chrome.alarms.create(\"alarm1\", {delayInMinutes:1});" 327 "chrome.alarms.create(\"alarm2\", {delayInMinutes:1});" 328 "setTimeout(function() {" 329 "chrome.alarms.clearAll(function (wasCleared) {" 330 "if (wasCleared) {" + 331 GetMessageJS(kSuccessMessage) + "}})}, 100)"; 332 } 333 334 private: 335 IMPLEMENT_REFCOUNTING(ClearAllAlarmsTestHandler); 336 DISALLOW_COPY_AND_ASSIGN(ClearAllAlarmsTestHandler); 337 }; 338 } // namespace 339 340 ALARMS_TEST_GROUP_MINIMAL(ClearAllAlarms, ClearAllAlarmsTestHandler) 341