• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
7 #include <sstream>
8 
9 #include "include/cef_parser.h"
10 
11 #include "tests/ceftests/test_util.h"
12 #include "tests/shared/browser/extension_util.h"
13 
14 #define TABS_TEST_GROUP_ALL(name, test_class) \
15   EXTENSION_TEST_GROUP_ALL(ChromeTabs##name, test_class)
16 #define TABS_TEST_GROUP_MINIMAL(name, test_class) \
17   EXTENSION_TEST_GROUP_MINIMAL(ChromeTabs##name, test_class)
18 
19 namespace {
20 
21 const char kMainBrowserURL[] = "https://test-extensions.com/chrome-tabs";
22 const char kExtensionPath[] = "tabs-extension";
23 const char kSuccessMessage[] = "success";
24 
25 // Base class for testing chrome.tabs methods.
26 // See https://developer.chrome.com/extensions/tabs
27 class TabsTestHandler : public ExtensionTestHandler {
28  public:
TabsTestHandler(RequestContextType request_context_type)29   explicit TabsTestHandler(RequestContextType request_context_type)
30       : ExtensionTestHandler(request_context_type),
31         create_main_browser_first_(false),
32         expect_get_active_browser_(true),
33         expect_success_in_main_browser_(true),
34         expected_api_call_count_(1),
35         got_get_active_browser_count_(0),
36         got_can_access_browser_count_(0) {}
37 
38   // CefExtensionHandler methods:
OnExtensionLoaded(CefRefPtr<CefExtension> extension)39   void OnExtensionLoaded(CefRefPtr<CefExtension> extension) override {
40     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
41     EXPECT_FALSE(got_extension_loaded_);
42     got_extension_loaded_.yes();
43 
44     // Verify |extension| contents.
45     EXPECT_FALSE(extension->GetIdentifier().empty());
46     EXPECT_STREQ(("extensions/" + std::string(kExtensionPath)).c_str(),
47                  client::extension_util::GetInternalExtensionResourcePath(
48                      extension->GetPath())
49                      .c_str());
50     TestDictionaryEqual(CreateManifest(), extension->GetManifest());
51 
52     EXPECT_FALSE(extension_);
53     extension_ = extension;
54 
55     if (create_main_browser_first_)
56       CreateBrowserForExtensionIfReady();
57     else
58       CreateBrowserForExtension();
59   }
60 
OnExtensionUnloaded(CefRefPtr<CefExtension> extension)61   void OnExtensionUnloaded(CefRefPtr<CefExtension> extension) override {
62     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
63     EXPECT_TRUE(extension_);
64     EXPECT_TRUE(extension_->IsSame(extension));
65     EXPECT_FALSE(got_extension_unloaded_);
66     got_extension_unloaded_.yes();
67     extension_ = nullptr;
68 
69     // Execute asynchronously so call stacks have a chance to unwind.
70     // Will close the browser windows.
71     CefPostTask(TID_UI, base::BindOnce(&TabsTestHandler::DestroyTest, this));
72   }
73 
GetActiveBrowser(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,bool include_incognito)74   CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
75                                          CefRefPtr<CefBrowser> browser,
76                                          bool include_incognito) override {
77     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
78     EXPECT_TRUE(extension_);
79     EXPECT_TRUE(extension_->IsSame(extension));
80     EXPECT_TRUE(main_browser_);
81 
82     EXPECT_LE(got_get_active_browser_count_, expected_api_call_count_);
83     got_get_active_browser_count_++;
84 
85     // Tabs APIs will operate on the main browser.
86     return main_browser_;
87   }
88 
CanAccessBrowser(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,bool include_incognito,CefRefPtr<CefBrowser> target_browser)89   bool CanAccessBrowser(CefRefPtr<CefExtension> extension,
90                         CefRefPtr<CefBrowser> browser,
91                         bool include_incognito,
92                         CefRefPtr<CefBrowser> target_browser) override {
93     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
94     EXPECT_TRUE(extension_);
95     EXPECT_TRUE(extension_->IsSame(extension));
96     EXPECT_TRUE(main_browser_);
97     EXPECT_TRUE(main_browser_->IsSame(target_browser));
98 
99     EXPECT_LE(got_can_access_browser_count_, expected_api_call_count_);
100     got_can_access_browser_count_++;
101 
102     return true;
103   }
104 
105   // CefLoadHandler methods:
OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)106   void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
107                             bool isLoading,
108                             bool canGoBack,
109                             bool canGoForward) override {
110     if (isLoading) {
111       // Keep a reference to both browsers.
112       if (browser->GetHost()->GetExtension()) {
113         EXPECT_FALSE(extension_browser_);
114         extension_browser_ = browser;
115       } else {
116         EXPECT_FALSE(main_browser_);
117         main_browser_ = browser;
118       }
119     } else {
120       const std::string& url = browser->GetMainFrame()->GetURL();
121       if (browser->GetHost()->GetExtension()) {
122         EXPECT_TRUE(browser->IsSame(extension_browser_));
123         EXPECT_STREQ(extension_url_.c_str(), url.c_str());
124       } else {
125         EXPECT_TRUE(browser->IsSame(main_browser_));
126         EXPECT_STREQ(kMainBrowserURL, url.c_str());
127       }
128     }
129   }
130 
131   // CefResourceRequestHandler methods:
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)132   CefRefPtr<CefResourceHandler> GetResourceHandler(
133       CefRefPtr<CefBrowser> browser,
134       CefRefPtr<CefFrame> frame,
135       CefRefPtr<CefRequest> request) override {
136     const std::string& url = request->GetURL();
137     if (url == kMainBrowserURL) {
138       EXPECT_TRUE(browser->IsSame(main_browser_));
139       EXPECT_FALSE(got_main_url_request_);
140       got_main_url_request_.yes();
141     } else if (url == extension_url_) {
142       EXPECT_TRUE(browser->IsSame(extension_browser_));
143       EXPECT_FALSE(got_extension_url_request_);
144       got_extension_url_request_.yes();
145     }
146 
147     // Handle the resource request.
148     return RoutingTestHandler::GetResourceHandler(browser, frame, request);
149   }
150 
151  protected:
OnAddMainBrowserResources()152   void OnAddMainBrowserResources() override {
153     AddResource(kMainBrowserURL, GetMainBrowserHTML(), "text/html");
154   }
155 
OnCreateMainBrowser()156   void OnCreateMainBrowser() override {
157     CreateBrowser(kMainBrowserURL, request_context());
158   }
159 
OnLoadExtensions()160   void OnLoadExtensions() override {
161     LoadExtension(kExtensionPath, CreateManifest());
162   }
163 
OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)164   bool OnMessage(CefRefPtr<CefBrowser> browser,
165                  const std::string& message) override {
166     if (message == "main_onload") {
167       // From body onLoad in the main browser.
168       EXPECT_TRUE(browser->IsSame(main_browser_));
169       EXPECT_FALSE(got_main_body_onload_);
170       got_main_body_onload_.yes();
171       if (create_main_browser_first_)
172         CreateBrowserForExtensionIfReady();
173       TriggerTabsApiJSFunctionIfReady();
174       return true;
175     }
176     if (message == "extension_onload") {
177       // From body onLoad in the extension browser.
178       EXPECT_TRUE(browser->IsSame(extension_browser_));
179       EXPECT_FALSE(got_extension_body_onload_);
180       got_extension_body_onload_.yes();
181       TriggerTabsApiJSFunctionIfReady();
182       return true;
183     }
184 
185     // The success message usually orginates from the logic in
186     // GetMainBrowserSuccessHEAD/BODY(). It may occasionally originate from the
187     // extension browser if we don't know how to detect success in the main
188     // browser.
189     if (expect_success_in_main_browser_) {
190       EXPECT_TRUE(browser->IsSame(main_browser_));
191     } else {
192       EXPECT_TRUE(browser->IsSame(extension_browser_));
193     }
194     EXPECT_FALSE(got_success_message_);
195     got_success_message_.yes();
196     EXPECT_STREQ(kSuccessMessage, message.c_str());
197     TriggerDestroyTest();
198     return true;
199   }
200 
OnDestroyTest()201   void OnDestroyTest() override {
202     main_browser_ = nullptr;
203     extension_browser_ = nullptr;
204 
205     EXPECT_TRUE(got_extension_loaded_);
206     EXPECT_TRUE(got_main_url_request_);
207     EXPECT_TRUE(got_extension_url_request_);
208     EXPECT_TRUE(got_main_body_onload_);
209     EXPECT_TRUE(got_extension_body_onload_);
210     EXPECT_TRUE(got_trigger_api_function_);
211     EXPECT_TRUE(got_success_message_);
212     EXPECT_TRUE(got_extension_unloaded_);
213 
214     if (expect_get_active_browser_) {
215       EXPECT_EQ(expected_api_call_count_, got_get_active_browser_count_);
216       EXPECT_EQ(0, got_can_access_browser_count_);
217     } else {
218       EXPECT_EQ(0, got_get_active_browser_count_);
219       EXPECT_EQ(expected_api_call_count_, got_can_access_browser_count_);
220     }
221   }
222 
223   // Create a manifest that grants access to the tabs API.
CreateManifest() const224   virtual CefRefPtr<CefDictionaryValue> CreateManifest() const {
225     ApiPermissionsList api_permissions;
226     api_permissions.push_back("tabs");
227     return CreateDefaultManifest(api_permissions);
228   }
229 
230   // Add resources in the extension browser.
OnAddExtensionResources(const std::string & origin)231   virtual void OnAddExtensionResources(const std::string& origin) {
232     extension_url_ = origin + "extension.html";
233     AddResource(extension_url_, GetExtensionHTML(), "text/html");
234   }
235 
236   // Returns the target tabId (null, or value >= 0).
GetTargetTabId() const237   virtual std::string GetTargetTabId() const { return "null"; }
238 
239   // Returns the logic in the main browser that triggers on success. It should
240   // execute GetMessageJS(kSuccessMessage).
GetMainBrowserSuccessHEAD() const241   virtual std::string GetMainBrowserSuccessHEAD() const {
242     return std::string();
243   }
GetMainBrowserSuccessBODY() const244   virtual std::string GetMainBrowserSuccessBODY() const {
245     return std::string();
246   }
247 
248   // Returns the HTML that will be loaded in the main browser.
GetMainBrowserHTML() const249   virtual std::string GetMainBrowserHTML() const {
250     return "<html><head>" + GetMainBrowserSuccessHEAD() +
251            "</head><body onLoad=" + GetMessageJS("main_onload") + ">Main" +
252            GetMainBrowserSuccessBODY() + "</body></html>";
253   }
254 
255   // Returns the chrome.tabs.* JS that is executed in the extension browser
256   // when the triggerTabsApi() JS function is called.
257   virtual std::string GetTabsApiJS() const = 0;
258 
259   // Returns the JS that will be loaded in the extension browser. This
260   // implements the triggerTabsApi() JS function called from
261   // TriggerTabsApiJSFunction().
GetExtensionJS() const262   virtual std::string GetExtensionJS() const {
263     return "function triggerTabsApi() {" + GetTabsApiJS() + "}";
264   }
265 
266   // Returns the HTML that will be loaded in the extension browser.
GetExtensionHTML() const267   virtual std::string GetExtensionHTML() const {
268     return "<html><head><script>" + GetExtensionJS() +
269            "</script></head><body onLoad=" + GetMessageJS("extension_onload") +
270            ">Extension</body></html>";
271   }
272 
TriggerDestroyTest()273   virtual void TriggerDestroyTest() {
274     // Execute asynchronously so call stacks have a chance to unwind.
275     CefPostTask(TID_UI, base::BindOnce(&TabsTestHandler::UnloadExtension, this,
276                                        extension_));
277   }
278 
extension() const279   CefRefPtr<CefExtension> extension() const { return extension_; }
extension_url() const280   std::string extension_url() const { return extension_url_; }
main_browser() const281   CefRefPtr<CefBrowser> main_browser() const { return main_browser_; }
extension_browser() const282   CefRefPtr<CefBrowser> extension_browser() const { return extension_browser_; }
283 
set_create_main_browser_first(bool val)284   void set_create_main_browser_first(bool val) {
285     create_main_browser_first_ = val;
286   }
set_expect_get_active_browser(bool val)287   void set_expect_get_active_browser(bool val) {
288     expect_get_active_browser_ = val;
289   }
set_expect_success_in_main_browser(bool val)290   void set_expect_success_in_main_browser(bool val) {
291     expect_success_in_main_browser_ = val;
292   }
set_expected_api_call_count(int val)293   void set_expected_api_call_count(int val) { expected_api_call_count_ = val; }
294 
got_success_message() const295   bool got_success_message() const { return got_success_message_; }
set_got_success_message()296   void set_got_success_message() { got_success_message_.yes(); }
297 
298  private:
CreateBrowserForExtensionIfReady()299   void CreateBrowserForExtensionIfReady() {
300     DCHECK(create_main_browser_first_);
301     if (extension_ && main_browser_)
302       CreateBrowserForExtension();
303   }
304 
CreateBrowserForExtension()305   void CreateBrowserForExtension() {
306     const std::string& identifier = extension_->GetIdentifier();
307     EXPECT_FALSE(identifier.empty());
308     const std::string& origin =
309         client::extension_util::GetExtensionOrigin(identifier);
310     EXPECT_FALSE(origin.empty());
311 
312     // Add extension resources.
313     OnAddExtensionResources(origin);
314 
315     // Create a browser to host the extension.
316     CreateBrowser(extension_url_, request_context());
317   }
318 
TriggerTabsApiJSFunctionIfReady()319   void TriggerTabsApiJSFunctionIfReady() {
320     if (got_main_body_onload_ && got_extension_body_onload_)
321       TriggerTabsApiJSFunction();
322   }
323 
TriggerTabsApiJSFunction()324   void TriggerTabsApiJSFunction() {
325     EXPECT_FALSE(got_trigger_api_function_);
326     got_trigger_api_function_.yes();
327 
328     extension_browser_->GetMainFrame()->ExecuteJavaScript("triggerTabsApi();",
329                                                           extension_url_, 0);
330   }
331 
332   // If true the main browser will be created before the extension browser.
333   // Otherwise the creation order is undefined.
334   bool create_main_browser_first_;
335 
336   // If true we expect GetActiveBrowser() but not CanAccessBrowser() to be
337   // called. Else visa-versa.
338   bool expect_get_active_browser_;
339 
340   // If true we expect the success message to be delivered in the main browser.
341   // Else expect it in the extension browser.
342   bool expect_success_in_main_browser_;
343 
344   // Number of expected calls to GetActiveBrowser or CanAccessBrowser. This
345   // should match the number of calls to chrome.tabs.* API functions in the
346   // test.
347   int expected_api_call_count_;
348 
349   CefRefPtr<CefExtension> extension_;
350   std::string extension_url_;
351   CefRefPtr<CefBrowser> main_browser_;
352   CefRefPtr<CefBrowser> extension_browser_;
353 
354   TrackCallback got_extension_loaded_;
355   TrackCallback got_main_url_request_;
356   TrackCallback got_extension_url_request_;
357   TrackCallback got_main_body_onload_;
358   TrackCallback got_extension_body_onload_;
359   TrackCallback got_trigger_api_function_;
360   TrackCallback got_success_message_;
361   TrackCallback got_extension_unloaded_;
362 
363   int got_get_active_browser_count_;
364   int got_can_access_browser_count_;
365 };
366 
367 //
368 // chrome.tabs.create tests.
369 //
370 
371 const char kCreateBrowserURL[] =
372     "https://test-extensions.com/chrome-tabs-create";
373 const char kTabCallbackMessage[] = "tab-callback";
374 const int kCreateTabIndex = 2;
375 
376 // Class for chrome.tabs.create tests.
377 class CreateTestHandler : public TabsTestHandler {
378  public:
CreateTestHandler(RequestContextType request_context_type)379   explicit CreateTestHandler(RequestContextType request_context_type)
380       : TabsTestHandler(request_context_type) {}
381 
OnBeforeBrowser(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,CefRefPtr<CefBrowser> active_browser,int index,const CefString & url,bool foreground,CefWindowInfo & windowInfo,CefRefPtr<CefClient> & client,CefBrowserSettings & settings)382   bool OnBeforeBrowser(CefRefPtr<CefExtension> extension,
383                        CefRefPtr<CefBrowser> browser,
384                        CefRefPtr<CefBrowser> active_browser,
385                        int index,
386                        const CefString& url,
387                        bool foreground,
388                        CefWindowInfo& windowInfo,
389                        CefRefPtr<CefClient>& client,
390                        CefBrowserSettings& settings) override {
391     EXPECT_TRUE(extension->IsSame(this->extension()));
392     EXPECT_TRUE(browser->IsSame(extension_browser()));
393     EXPECT_TRUE(active_browser->IsSame(main_browser()));
394     EXPECT_EQ(kCreateTabIndex, index);
395     EXPECT_STREQ(kCreateBrowserURL, url.ToString().c_str());
396     EXPECT_TRUE(foreground);
397     EXPECT_TRUE(client);
398 
399     EXPECT_FALSE(got_on_before_browser_);
400     got_on_before_browser_.yes();
401 
402     return false;
403   }
404 
OnAddMainBrowserResources()405   void OnAddMainBrowserResources() override {
406     AddResource(kCreateBrowserURL, GetCreatedBrowserHTML(), "text/html");
407 
408     TabsTestHandler::OnAddMainBrowserResources();
409   }
410 
OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)411   void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
412                             bool isLoading,
413                             bool canGoBack,
414                             bool canGoForward) override {
415     if (extension_browser() && main_browser()) {
416       if (isLoading) {
417         // Keep a reference to the newly created browser.
418         EXPECT_FALSE(created_browser_);
419         created_browser_ = browser;
420         return;
421       } else {
422         const std::string& url = browser->GetMainFrame()->GetURL();
423         if (url == kCreateBrowserURL) {
424           EXPECT_TRUE(browser->IsSame(created_browser_));
425           return;
426         }
427       }
428     }
429 
430     TabsTestHandler::OnLoadingStateChange(browser, isLoading, canGoBack,
431                                           canGoForward);
432   }
433 
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)434   CefRefPtr<CefResourceHandler> GetResourceHandler(
435       CefRefPtr<CefBrowser> browser,
436       CefRefPtr<CefFrame> frame,
437       CefRefPtr<CefRequest> request) override {
438     const std::string& url = request->GetURL();
439     if (url == kCreateBrowserURL) {
440       EXPECT_TRUE(browser->IsSame(created_browser_));
441       EXPECT_FALSE(got_create_browser_url_request_);
442       got_create_browser_url_request_.yes();
443     }
444 
445     return TabsTestHandler::GetResourceHandler(browser, frame, request);
446   }
447 
448  protected:
GetTabsApiJS() const449   std::string GetTabsApiJS() const override {
450     std::stringstream ss;
451     ss << kCreateTabIndex;
452 
453     return "chrome.tabs.create({url: \"" + std::string(kCreateBrowserURL) +
454            "\", index: " + ss.str() +
455            "}, function(tab) { window.testQuery({request:'" +
456            kTabCallbackMessage + ":' + JSON.stringify(tab)}); });";
457   }
458 
OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)459   bool OnMessage(CefRefPtr<CefBrowser> browser,
460                  const std::string& message) override {
461     if (message.find(kTabCallbackMessage) != std::string::npos) {
462       EXPECT_TRUE(browser->IsSame(extension_browser()));
463       EXPECT_FALSE(got_tab_callback_message_);
464       got_tab_callback_message_.yes();
465 
466       // Verify the contents of the Tab object.
467       const std::string& json_str =
468           message.substr(strlen(kTabCallbackMessage) + 1);
469       CefRefPtr<CefValue> obj = CefParseJSON(json_str, JSON_PARSER_RFC);
470       EXPECT_TRUE(obj);
471       EXPECT_EQ(VTYPE_DICTIONARY, obj->GetType());
472       CefRefPtr<CefDictionaryValue> dict = obj->GetDictionary();
473 
474       int index = dict->GetInt("index");
475       EXPECT_EQ(kCreateTabIndex, index);
476 
477       int id = dict->GetInt("id");
478       int windowId = dict->GetInt("windowId");
479       EXPECT_EQ(created_browser_->GetIdentifier(), id);
480       EXPECT_EQ(created_browser_->GetIdentifier(), windowId);
481 
482       const std::string& url = dict->GetString("url");
483       EXPECT_STREQ(kCreateBrowserURL, url.c_str());
484 
485       TriggerDestroyTestIfReady();
486       return true;
487     } else if (message == kSuccessMessage) {
488       // Overriding default kSuccessMessage handling.
489       EXPECT_TRUE(browser->IsSame(created_browser_));
490       EXPECT_FALSE(got_success_message());
491       set_got_success_message();
492       TriggerDestroyTestIfReady();
493       return true;
494     }
495 
496     return TabsTestHandler::OnMessage(browser, message);
497   }
498 
OnDestroyTest()499   void OnDestroyTest() override {
500     created_browser_ = nullptr;
501 
502     EXPECT_TRUE(got_on_before_browser_);
503     EXPECT_TRUE(got_create_browser_url_request_);
504     EXPECT_TRUE(got_tab_callback_message_);
505 
506     TabsTestHandler::OnDestroyTest();
507   }
508 
509  private:
GetCreatedBrowserHTML()510   std::string GetCreatedBrowserHTML() {
511     return "<html><body onLoad=" + GetMessageJS(kSuccessMessage) +
512            ">Created</body></html>";
513   }
514 
TriggerDestroyTestIfReady()515   void TriggerDestroyTestIfReady() {
516     if (got_tab_callback_message_ && got_success_message())
517       TriggerDestroyTest();
518   }
519 
520   CefRefPtr<CefBrowser> created_browser_;
521 
522   TrackCallback got_on_before_browser_;
523   TrackCallback got_create_browser_url_request_;
524   TrackCallback got_tab_callback_message_;
525 
526   IMPLEMENT_REFCOUNTING(CreateTestHandler);
527   DISALLOW_COPY_AND_ASSIGN(CreateTestHandler);
528 };
529 
530 }  // namespace
531 
532 TABS_TEST_GROUP_ALL(Create, CreateTestHandler)
533 
534 namespace {
535 
536 //
537 // chrome.tabs.executeScript tests.
538 //
539 
540 // Base class for chrome.tabs.executeScript tests.
541 class ExecuteScriptTestHandler : public TabsTestHandler {
542  public:
ExecuteScriptTestHandler(RequestContextType request_context_type)543   explicit ExecuteScriptTestHandler(RequestContextType request_context_type)
544       : TabsTestHandler(request_context_type) {}
545 
546  protected:
GetMainBrowserSuccessHEAD() const547   std::string GetMainBrowserSuccessHEAD() const override {
548     return "<script>function onTrigger() {" + GetMessageJS(kSuccessMessage) +
549            "}</script>";
550   }
551 
552   // Returns the code that will be injected as a content script.
GetContentScriptJS() const553   virtual std::string GetContentScriptJS() const {
554     // Execute the onTrigger() JS function.
555     return "var s = document.createElement('script');"
556            "s.textContent = 'onTrigger();';"
557            "document.head.appendChild(s);";
558   }
559 
GetTabsApiJS() const560   std::string GetTabsApiJS() const override {
561     return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" +
562            GetContentScriptJS() + "\"});";
563   }
564 };
565 
566 // Test for chrome.tabs.executeScript with a null tabId value.
567 class ExecuteScriptNullTabTestHandler : public ExecuteScriptTestHandler {
568  public:
ExecuteScriptNullTabTestHandler(RequestContextType request_context_type)569   explicit ExecuteScriptNullTabTestHandler(
570       RequestContextType request_context_type)
571       : ExecuteScriptTestHandler(request_context_type) {}
572 
573  private:
574   IMPLEMENT_REFCOUNTING(ExecuteScriptNullTabTestHandler);
575   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptNullTabTestHandler);
576 };
577 
578 }  // namespace
579 
580 TABS_TEST_GROUP_ALL(ExecuteScriptNullTab, ExecuteScriptNullTabTestHandler)
581 
582 namespace {
583 
584 // Test for chrome.tabs.executeScript with an explicit tabId value.
585 class ExecuteScriptExplicitTabTestHandler : public ExecuteScriptTestHandler {
586  public:
ExecuteScriptExplicitTabTestHandler(RequestContextType request_context_type)587   explicit ExecuteScriptExplicitTabTestHandler(
588       RequestContextType request_context_type)
589       : ExecuteScriptTestHandler(request_context_type) {
590     // Create the main browser first so we can retrieve the id.
591     set_create_main_browser_first(true);
592     // When a tabId is specified we should get a call to CanAccessBrowser
593     // instead of GetActiveBrowser.
594     set_expect_get_active_browser(false);
595   }
596 
597  protected:
GetTargetTabId() const598   std::string GetTargetTabId() const override {
599     DCHECK(main_browser());
600     std::stringstream ss;
601     ss << main_browser()->GetIdentifier();
602     return ss.str();
603   }
604 
605  private:
606   IMPLEMENT_REFCOUNTING(ExecuteScriptExplicitTabTestHandler);
607   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptExplicitTabTestHandler);
608 };
609 
610 }  // namespace
611 
612 TABS_TEST_GROUP_ALL(ExecuteScriptExplicitTab,
613                     ExecuteScriptExplicitTabTestHandler)
614 
615 namespace {
616 
617 // Test for chrome.tabs.executeScript with a file argument loading a content
618 // script.
619 class ExecuteScriptFileTestHandler : public ExecuteScriptTestHandler {
620  public:
ExecuteScriptFileTestHandler(RequestContextType request_context_type)621   explicit ExecuteScriptFileTestHandler(RequestContextType request_context_type)
622       : ExecuteScriptTestHandler(request_context_type) {}
623 
624   // CefExtensionHandler methods:
GetExtensionResource(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,const CefString & file,CefRefPtr<CefGetExtensionResourceCallback> callback)625   bool GetExtensionResource(
626       CefRefPtr<CefExtension> extension,
627       CefRefPtr<CefBrowser> browser,
628       const CefString& file,
629       CefRefPtr<CefGetExtensionResourceCallback> callback) override {
630     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
631     EXPECT_TRUE(this->extension());
632     EXPECT_TRUE(this->extension()->IsSame(extension));
633 
634     if (file == "script.js") {
635       EXPECT_FALSE(got_get_extension_resource_);
636       got_get_extension_resource_.yes();
637 
638       const std::string& content = GetContentScriptJS();
639       CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
640           const_cast<char*>(content.data()), content.size());
641       callback->Continue(stream);
642       return true;
643     }
644 
645     EXPECT_FALSE(true);  // Not reached.
646     return false;
647   }
648 
649  protected:
GetTabsApiJS() const650   std::string GetTabsApiJS() const override {
651     return "chrome.tabs.executeScript(" + GetTargetTabId() +
652            ", {file:\"script.js\"});";
653   }
654 
OnDestroyTest()655   void OnDestroyTest() override {
656     ExecuteScriptTestHandler::OnDestroyTest();
657     EXPECT_TRUE(got_get_extension_resource_);
658   }
659 
660  private:
661   TrackCallback got_get_extension_resource_;
662 
663   IMPLEMENT_REFCOUNTING(ExecuteScriptFileTestHandler);
664   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptFileTestHandler);
665 };
666 
667 }  // namespace
668 
669 TABS_TEST_GROUP_ALL(ExecuteScriptFile, ExecuteScriptFileTestHandler)
670 
671 namespace {
672 
673 // Test for chrome.tabs.executeScript with a callback argument.
674 class ExecuteScriptCallbackTestHandler : public ExecuteScriptTestHandler {
675  public:
ExecuteScriptCallbackTestHandler(RequestContextType request_context_type)676   explicit ExecuteScriptCallbackTestHandler(
677       RequestContextType request_context_type)
678       : ExecuteScriptTestHandler(request_context_type) {}
679 
680  protected:
OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)681   bool OnMessage(CefRefPtr<CefBrowser> browser,
682                  const std::string& message) override {
683     if (message == "callback") {
684       EXPECT_FALSE(got_callback_message_);
685       got_callback_message_.yes();
686       EXPECT_TRUE(browser->IsSame(extension_browser()));
687       TriggerDestroyTest();
688       return true;
689     }
690     return ExecuteScriptTestHandler::OnMessage(browser, message);
691   }
692 
GetTabsApiJS() const693   std::string GetTabsApiJS() const override {
694     return "chrome.tabs.executeScript(" + GetTargetTabId() + ", {code:\"" +
695            GetContentScriptJS() + "\"}, function(results) {" +
696            GetMessageJS("callback") + "});";
697   }
698 
TriggerDestroyTest()699   void TriggerDestroyTest() override {
700     // Only destroy the test if we got both callbacks.
701     if (got_callback_message_ && got_success_message()) {
702       ExecuteScriptTestHandler::TriggerDestroyTest();
703     }
704   }
705 
OnDestroyTest()706   void OnDestroyTest() override {
707     ExecuteScriptTestHandler::OnDestroyTest();
708     EXPECT_TRUE(got_callback_message_);
709   }
710 
711  private:
712   TrackCallback got_callback_message_;
713 
714   IMPLEMENT_REFCOUNTING(ExecuteScriptCallbackTestHandler);
715   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptCallbackTestHandler);
716 };
717 
718 }  // namespace
719 
720 TABS_TEST_GROUP_MINIMAL(ExecuteScriptCallback, ExecuteScriptCallbackTestHandler)
721 
722 namespace {
723 
724 // Test for chrome.tabs.executeScript with execution occuring from a separate
725 // resource script.
726 class ExecuteScriptResourceTabTestHandler : public ExecuteScriptTestHandler {
727  public:
ExecuteScriptResourceTabTestHandler(RequestContextType request_context_type)728   explicit ExecuteScriptResourceTabTestHandler(
729       RequestContextType request_context_type)
730       : ExecuteScriptTestHandler(request_context_type) {}
731 
732   // CefResourceRequestHandler methods:
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)733   CefRefPtr<CefResourceHandler> GetResourceHandler(
734       CefRefPtr<CefBrowser> browser,
735       CefRefPtr<CefFrame> frame,
736       CefRefPtr<CefRequest> request) override {
737     const std::string& url = request->GetURL();
738     if (url == resource_url_) {
739       EXPECT_TRUE(browser->IsSame(extension_browser()));
740       EXPECT_FALSE(got_resource_url_request_);
741       got_resource_url_request_.yes();
742     }
743 
744     return ExecuteScriptTestHandler::GetResourceHandler(browser, frame,
745                                                         request);
746   }
747 
748  protected:
OnAddExtensionResources(const std::string & origin)749   void OnAddExtensionResources(const std::string& origin) override {
750     ExecuteScriptTestHandler::OnAddExtensionResources(origin);
751     resource_url_ = origin + "resource.js";
752     AddResource(resource_url_, GetExtensionJS(), "text/javascript");
753   }
754 
GetExtensionHTML() const755   std::string GetExtensionHTML() const override {
756     return "<html><head><script src=\"resource.js\"></script></head><body "
757            "onLoad=" +
758            GetMessageJS("extension_onload") + ">Extension</body></html>";
759   }
760 
OnDestroyTest()761   void OnDestroyTest() override {
762     ExecuteScriptTestHandler::OnDestroyTest();
763     EXPECT_TRUE(got_resource_url_request_);
764   }
765 
766  private:
767   std::string resource_url_;
768   TrackCallback got_resource_url_request_;
769 
770   IMPLEMENT_REFCOUNTING(ExecuteScriptResourceTabTestHandler);
771   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptResourceTabTestHandler);
772 };
773 
774 }  // namespace
775 
776 TABS_TEST_GROUP_MINIMAL(ExecuteScriptResource,
777                         ExecuteScriptResourceTabTestHandler)
778 
779 //
780 // chrome.tabs.insertCSS tests.
781 //
782 
783 namespace {
784 
785 // Base class for chrome.tabs.insertCSS tests.
786 class InsertCSSTestHandler : public TabsTestHandler {
787  public:
InsertCSSTestHandler(RequestContextType request_context_type)788   explicit InsertCSSTestHandler(RequestContextType request_context_type)
789       : TabsTestHandler(request_context_type) {}
790 
791  protected:
GetMainBrowserSuccessBODY() const792   std::string GetMainBrowserSuccessBODY() const override {
793     // We can't use a MutationObserver here because insertCSS does not modify
794     // the style attribute. We could detect the change by tracking modifications
795     // to document.styleSheets but that's complicated. Use a simple timer loop
796     // implementation calling getComputedStyle instead.
797     return "<script>var interval = setInterval(function() {"
798            "if (window.getComputedStyle(document.body,null)."
799            "getPropertyValue('background-color') == 'rgb(255, 0, 0)') {" +
800            GetMessageJS(kSuccessMessage) +
801            "clearInterval(interval);}}, 100);</script>";
802   }
803 
804   // Returns the code that will be injected as a content script.
GetContentScriptCSS() const805   virtual std::string GetContentScriptCSS() const {
806     return "body{background-color:red}";
807   }
808 
GetTabsApiJS() const809   std::string GetTabsApiJS() const override {
810     return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" +
811            GetContentScriptCSS() + "\"});";
812   }
813 };
814 
815 // Test for chrome.tabs.insertCSS with a null tabId value.
816 class InsertCSSNullTabTestHandler : public InsertCSSTestHandler {
817  public:
InsertCSSNullTabTestHandler(RequestContextType request_context_type)818   explicit InsertCSSNullTabTestHandler(RequestContextType request_context_type)
819       : InsertCSSTestHandler(request_context_type) {}
820 
821  private:
822   IMPLEMENT_REFCOUNTING(InsertCSSNullTabTestHandler);
823   DISALLOW_COPY_AND_ASSIGN(InsertCSSNullTabTestHandler);
824 };
825 
826 }  // namespace
827 
828 TABS_TEST_GROUP_ALL(InsertCSSNullTab, InsertCSSNullTabTestHandler)
829 
830 namespace {
831 
832 // Test for chrome.tabs.insertCSS with an explicit tabId value.
833 class InsertCSSExplicitTabTestHandler : public InsertCSSTestHandler {
834  public:
InsertCSSExplicitTabTestHandler(RequestContextType request_context_type)835   explicit InsertCSSExplicitTabTestHandler(
836       RequestContextType request_context_type)
837       : InsertCSSTestHandler(request_context_type) {
838     // Create the main browser first so we can retrieve the id.
839     set_create_main_browser_first(true);
840     // When a tabId is specified we should get a call to CanAccessBrowser
841     // instead of GetActiveBrowser.
842     set_expect_get_active_browser(false);
843   }
844 
845  protected:
GetTargetTabId() const846   std::string GetTargetTabId() const override {
847     DCHECK(main_browser());
848     std::stringstream ss;
849     ss << main_browser()->GetIdentifier();
850     return ss.str();
851   }
852 
853  private:
854   IMPLEMENT_REFCOUNTING(InsertCSSExplicitTabTestHandler);
855   DISALLOW_COPY_AND_ASSIGN(InsertCSSExplicitTabTestHandler);
856 };
857 
858 }  // namespace
859 
860 TABS_TEST_GROUP_ALL(InsertCSSExplicitTab, InsertCSSExplicitTabTestHandler)
861 
862 namespace {
863 
864 // Test for chrome.tabs.insertCSS with a file argument loading a content
865 // script.
866 class InsertCSSFileTestHandler : public InsertCSSTestHandler {
867  public:
InsertCSSFileTestHandler(RequestContextType request_context_type)868   explicit InsertCSSFileTestHandler(RequestContextType request_context_type)
869       : InsertCSSTestHandler(request_context_type) {}
870 
871   // CefExtensionHandler methods:
GetExtensionResource(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,const CefString & file,CefRefPtr<CefGetExtensionResourceCallback> callback)872   bool GetExtensionResource(
873       CefRefPtr<CefExtension> extension,
874       CefRefPtr<CefBrowser> browser,
875       const CefString& file,
876       CefRefPtr<CefGetExtensionResourceCallback> callback) override {
877     EXPECT_TRUE(CefCurrentlyOn(TID_UI));
878     EXPECT_TRUE(this->extension());
879     EXPECT_TRUE(this->extension()->IsSame(extension));
880 
881     if (file == "script.css") {
882       EXPECT_FALSE(got_get_extension_resource_);
883       got_get_extension_resource_.yes();
884 
885       const std::string& content = GetContentScriptCSS();
886       CefRefPtr<CefStreamReader> stream = CefStreamReader::CreateForData(
887           const_cast<char*>(content.data()), content.size());
888       callback->Continue(stream);
889       return true;
890     }
891 
892     EXPECT_FALSE(true);  // Not reached.
893     return false;
894   }
895 
896  protected:
GetTabsApiJS() const897   std::string GetTabsApiJS() const override {
898     return "chrome.tabs.insertCSS(" + GetTargetTabId() +
899            ", {file:\"script.css\"});";
900   }
901 
OnDestroyTest()902   void OnDestroyTest() override {
903     InsertCSSTestHandler::OnDestroyTest();
904     EXPECT_TRUE(got_get_extension_resource_);
905   }
906 
907  private:
908   TrackCallback got_get_extension_resource_;
909 
910   IMPLEMENT_REFCOUNTING(InsertCSSFileTestHandler);
911   DISALLOW_COPY_AND_ASSIGN(InsertCSSFileTestHandler);
912 };
913 
914 }  // namespace
915 
916 TABS_TEST_GROUP_ALL(InsertCSSFile, InsertCSSFileTestHandler)
917 
918 namespace {
919 
920 // Test for chrome.tabs.insertCSS with a callback argument.
921 class InsertCSSCallbackTestHandler : public InsertCSSTestHandler {
922  public:
InsertCSSCallbackTestHandler(RequestContextType request_context_type)923   explicit InsertCSSCallbackTestHandler(RequestContextType request_context_type)
924       : InsertCSSTestHandler(request_context_type) {}
925 
926  protected:
OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)927   bool OnMessage(CefRefPtr<CefBrowser> browser,
928                  const std::string& message) override {
929     if (message == "callback") {
930       EXPECT_FALSE(got_callback_message_);
931       got_callback_message_.yes();
932       EXPECT_TRUE(browser->IsSame(extension_browser()));
933       TriggerDestroyTest();
934       return true;
935     }
936     return InsertCSSTestHandler::OnMessage(browser, message);
937   }
938 
GetTabsApiJS() const939   std::string GetTabsApiJS() const override {
940     return "chrome.tabs.insertCSS(" + GetTargetTabId() + ", {code:\"" +
941            GetContentScriptCSS() + "\"}, function(results) {" +
942            GetMessageJS("callback") + "});";
943   }
944 
TriggerDestroyTest()945   void TriggerDestroyTest() override {
946     // Only destroy the test if we got both callbacks.
947     if (got_callback_message_ && got_success_message()) {
948       InsertCSSTestHandler::TriggerDestroyTest();
949     }
950   }
951 
OnDestroyTest()952   void OnDestroyTest() override {
953     InsertCSSTestHandler::OnDestroyTest();
954     EXPECT_TRUE(got_callback_message_);
955   }
956 
957  private:
958   TrackCallback got_callback_message_;
959 
960   IMPLEMENT_REFCOUNTING(InsertCSSCallbackTestHandler);
961   DISALLOW_COPY_AND_ASSIGN(InsertCSSCallbackTestHandler);
962 };
963 
964 }  // namespace
965 
966 TABS_TEST_GROUP_MINIMAL(InsertCSSCallback, InsertCSSCallbackTestHandler)
967 
968 namespace {
969 
970 // Test for chrome.tabs.insertCSS with execution occuring from a separate
971 // resource script.
972 class InsertCSSResourceTabTestHandler : public InsertCSSTestHandler {
973  public:
InsertCSSResourceTabTestHandler(RequestContextType request_context_type)974   explicit InsertCSSResourceTabTestHandler(
975       RequestContextType request_context_type)
976       : InsertCSSTestHandler(request_context_type) {}
977 
978   // CefResourceRequestHandler methods:
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)979   CefRefPtr<CefResourceHandler> GetResourceHandler(
980       CefRefPtr<CefBrowser> browser,
981       CefRefPtr<CefFrame> frame,
982       CefRefPtr<CefRequest> request) override {
983     const std::string& url = request->GetURL();
984     if (url == resource_url_) {
985       EXPECT_TRUE(browser->IsSame(extension_browser()));
986       EXPECT_FALSE(got_resource_url_request_);
987       got_resource_url_request_.yes();
988     }
989 
990     return InsertCSSTestHandler::GetResourceHandler(browser, frame, request);
991   }
992 
993  protected:
OnAddExtensionResources(const std::string & origin)994   void OnAddExtensionResources(const std::string& origin) override {
995     InsertCSSTestHandler::OnAddExtensionResources(origin);
996     resource_url_ = origin + "resource.js";
997     AddResource(resource_url_, GetExtensionJS(), "text/javascript");
998   }
999 
GetExtensionHTML() const1000   std::string GetExtensionHTML() const override {
1001     return "<html><head><script src=\"resource.js\"></script></head><body "
1002            "onLoad=" +
1003            GetMessageJS("extension_onload") + ">Extension</body></html>";
1004   }
1005 
OnDestroyTest()1006   void OnDestroyTest() override {
1007     InsertCSSTestHandler::OnDestroyTest();
1008     EXPECT_TRUE(got_resource_url_request_);
1009   }
1010 
1011  private:
1012   std::string resource_url_;
1013   TrackCallback got_resource_url_request_;
1014 
1015   IMPLEMENT_REFCOUNTING(InsertCSSResourceTabTestHandler);
1016   DISALLOW_COPY_AND_ASSIGN(InsertCSSResourceTabTestHandler);
1017 };
1018 
1019 }  // namespace
1020 
1021 TABS_TEST_GROUP_MINIMAL(InsertCSSResource, InsertCSSResourceTabTestHandler)
1022 
1023 //
1024 // chrome.tabs.setZoom/getZoom tests.
1025 //
1026 
1027 namespace {
1028 
1029 // Base class for chrome.tabs.setZoom/getZoom tests.
1030 class ZoomTestHandler : public TabsTestHandler {
1031  public:
ZoomTestHandler(RequestContextType request_context_type)1032   explicit ZoomTestHandler(RequestContextType request_context_type)
1033       : TabsTestHandler(request_context_type) {
1034     // We call API functions three times in this handler.
1035     set_expected_api_call_count(3);
1036   }
1037 
1038  protected:
GetMainBrowserSuccessHEAD() const1039   std::string GetMainBrowserSuccessHEAD() const override {
1040     return "<script>var orig_width = window.innerWidth;</script>";
1041   }
1042 
GetMainBrowserSuccessBODY() const1043   std::string GetMainBrowserSuccessBODY() const override {
1044     // We can't directly detect zoom changes, so instead we look for changes
1045     // in window.innerWidth.
1046     return "<script>var interval = setInterval(function() {"
1047            "if (window.innerWidth != orig_width) {" +
1048            GetMessageJS(kSuccessMessage) +
1049            "clearInterval(interval);}}, 100);</script>";
1050   }
1051 
GetTabsApiJS() const1052   std::string GetTabsApiJS() const override {
1053     // Results in a change to window.innerWidth.
1054     return "chrome.tabs.setZoom(" + GetTargetTabId() + ", 2.0);";
1055   }
1056 
OnMessage(CefRefPtr<CefBrowser> browser,const std::string & message)1057   bool OnMessage(CefRefPtr<CefBrowser> browser,
1058                  const std::string& message) override {
1059     if (message == "restored") {
1060       EXPECT_TRUE(browser->IsSame(extension_browser()));
1061       EXPECT_FALSE(got_restored_message_);
1062       got_restored_message_.yes();
1063       // Destroy the test for real.
1064       TabsTestHandler::TriggerDestroyTest();
1065       return true;
1066     }
1067     return TabsTestHandler::OnMessage(browser, message);
1068   }
1069 
TriggerDestroyTest()1070   void TriggerDestroyTest() override {
1071     // Before destroying the test we need to restore the zoom factor so that
1072     // it doesn't persist in the RequestContext. This also tests the callback
1073     // argument and the getZoom function so there's no need to do that
1074     // separately.
1075     extension_browser()->GetMainFrame()->ExecuteJavaScript(
1076         "chrome.tabs.setZoom(" + GetTargetTabId() + ", 1.0, function() {" +
1077             "chrome.tabs.getZoom(" + GetTargetTabId() +
1078             ", function(zoomFactor) { if (zoomFactor == 1.0) {" +
1079             GetMessageJS("restored") + "}})});",
1080         extension_url(), 0);
1081   }
1082 
OnDestroyTest()1083   void OnDestroyTest() override {
1084     TabsTestHandler::OnDestroyTest();
1085     EXPECT_TRUE(got_restored_message_);
1086   }
1087 
1088  private:
1089   TrackCallback got_restored_message_;
1090 };
1091 
1092 // Test for chrome.tabs.setZoom/getZoom with a null tabId value.
1093 class ZoomNullTabTestHandler : public ZoomTestHandler {
1094  public:
ZoomNullTabTestHandler(RequestContextType request_context_type)1095   explicit ZoomNullTabTestHandler(RequestContextType request_context_type)
1096       : ZoomTestHandler(request_context_type) {}
1097 
1098  private:
1099   IMPLEMENT_REFCOUNTING(ZoomNullTabTestHandler);
1100   DISALLOW_COPY_AND_ASSIGN(ZoomNullTabTestHandler);
1101 };
1102 
1103 }  // namespace
1104 
1105 TABS_TEST_GROUP_ALL(ZoomNullTab, ZoomNullTabTestHandler)
1106 
1107 namespace {
1108 
1109 // Test for chrome.tabs.setZoom/getZoom with an explicit tabId value.
1110 class ZoomExplicitTabTestHandler : public ZoomTestHandler {
1111  public:
ZoomExplicitTabTestHandler(RequestContextType request_context_type)1112   explicit ZoomExplicitTabTestHandler(RequestContextType request_context_type)
1113       : ZoomTestHandler(request_context_type) {
1114     // Create the main browser first so we can retrieve the id.
1115     set_create_main_browser_first(true);
1116     // When a tabId is specified we should get a call to CanAccessBrowser
1117     // instead of GetActiveBrowser.
1118     set_expect_get_active_browser(false);
1119   }
1120 
1121  protected:
GetTargetTabId() const1122   std::string GetTargetTabId() const override {
1123     DCHECK(main_browser());
1124     std::stringstream ss;
1125     ss << main_browser()->GetIdentifier();
1126     return ss.str();
1127   }
1128 
1129  private:
1130   IMPLEMENT_REFCOUNTING(ZoomExplicitTabTestHandler);
1131   DISALLOW_COPY_AND_ASSIGN(ZoomExplicitTabTestHandler);
1132 };
1133 
1134 }  // namespace
1135 
1136 TABS_TEST_GROUP_ALL(ZoomExplicitTab, ZoomExplicitTabTestHandler)
1137 
1138 //
1139 // chrome.tabs.setZoomSettings/getZoomSettings tests.
1140 //
1141 
1142 namespace {
1143 
1144 // Base class for chrome.tabs.setZoomSettings/getZoomSettings tests.
1145 class ZoomSettingsTestHandler : public TabsTestHandler {
1146  public:
ZoomSettingsTestHandler(RequestContextType request_context_type)1147   explicit ZoomSettingsTestHandler(RequestContextType request_context_type)
1148       : TabsTestHandler(request_context_type) {
1149     // We call API functions two times in this handler.
1150     set_expected_api_call_count(2);
1151     // Success message will be delivered in the extension browser because we
1152     // don't know how to detect zoom settings changes in the main browser.
1153     set_expect_success_in_main_browser(false);
1154   }
1155 
1156  protected:
GetTabsApiJS() const1157   std::string GetTabsApiJS() const override {
1158     // Set and restore the zoom settings. This also tests the callback argument
1159     // and the getZoomSettings function so there's no need to do that
1160     // separately. This is safe because zoom settings are not persisted in the
1161     // RequestContext across navigations.
1162     return "chrome.tabs.setZoomSettings(" + GetTargetTabId() +
1163            ", {mode: 'manual', scope: 'per-tab'}, function() {" +
1164            "chrome.tabs.getZoomSettings(" + GetTargetTabId() +
1165            ", function(zoomSettings) { if (zoomSettings.mode == 'manual' "
1166            "&& zoomSettings.scope == 'per-tab') {" +
1167            GetMessageJS(kSuccessMessage) + "}})});";
1168   }
1169 };
1170 
1171 // Test for chrome.tabs.setZoomSettings/getZoomSettings with a null tabId value.
1172 class ZoomSettingsNullTabTestHandler : public ZoomSettingsTestHandler {
1173  public:
ZoomSettingsNullTabTestHandler(RequestContextType request_context_type)1174   explicit ZoomSettingsNullTabTestHandler(
1175       RequestContextType request_context_type)
1176       : ZoomSettingsTestHandler(request_context_type) {}
1177 
1178  private:
1179   IMPLEMENT_REFCOUNTING(ZoomSettingsNullTabTestHandler);
1180   DISALLOW_COPY_AND_ASSIGN(ZoomSettingsNullTabTestHandler);
1181 };
1182 
1183 }  // namespace
1184 
1185 TABS_TEST_GROUP_ALL(ZoomSettingsNullTab, ZoomSettingsNullTabTestHandler)
1186 
1187 namespace {
1188 
1189 // Test for chrome.tabs.setZoomSettings/getZoomSettings with an explicit tabId
1190 // value.
1191 class ZoomSettingsExplicitTabTestHandler : public ZoomSettingsTestHandler {
1192  public:
ZoomSettingsExplicitTabTestHandler(RequestContextType request_context_type)1193   explicit ZoomSettingsExplicitTabTestHandler(
1194       RequestContextType request_context_type)
1195       : ZoomSettingsTestHandler(request_context_type) {
1196     // Create the main browser first so we can retrieve the id.
1197     set_create_main_browser_first(true);
1198     // When a tabId is specified we should get a call to CanAccessBrowser
1199     // instead of GetActiveBrowser.
1200     set_expect_get_active_browser(false);
1201   }
1202 
1203  protected:
GetTargetTabId() const1204   std::string GetTargetTabId() const override {
1205     DCHECK(main_browser());
1206     std::stringstream ss;
1207     ss << main_browser()->GetIdentifier();
1208     return ss.str();
1209   }
1210 
1211  private:
1212   IMPLEMENT_REFCOUNTING(ZoomSettingsExplicitTabTestHandler);
1213   DISALLOW_COPY_AND_ASSIGN(ZoomSettingsExplicitTabTestHandler);
1214 };
1215 
1216 }  // namespace
1217 
1218 TABS_TEST_GROUP_ALL(ZoomSettingsExplicitTab, ZoomSettingsExplicitTabTestHandler)
1219