Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Side by Side Diff: chrome/browser/extensions/process_manager_browsertest.cc

Issue 1413853005: Track all extension frames in ProcessManager, inspect extensionoptions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nits, treat hosted apps as extensions, no test flakiness Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/browser/process_manager.h" 5 #include "extensions/browser/process_manager.h"
6 6
7 #include "base/callback.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/strings/stringprintf.h"
7 #include "chrome/browser/extensions/browser_action_test_util.h" 11 #include "chrome/browser/extensions/browser_action_test_util.h"
8 #include "chrome/browser/extensions/extension_browsertest.h" 12 #include "chrome/browser/extensions/extension_browsertest.h"
9 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/test_extension_dir.h"
10 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/common/extensions/extension_process_policy.h"
11 #include "chrome/test/base/in_process_browser_test.h" 17 #include "chrome/test/base/in_process_browser_test.h"
12 #include "chrome/test/base/ui_test_utils.h" 18 #include "chrome/test/base/ui_test_utils.h"
13 #include "content/public/browser/notification_service.h" 19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/web_contents.h" 21 #include "content/public/browser/web_contents.h"
22 #include "content/public/test/browser_test_utils.h"
15 #include "content/public/test/test_utils.h" 23 #include "content/public/test/test_utils.h"
24 #include "extensions/common/value_builder.h"
25 #include "extensions/test/background_page_watcher.h"
26 #include "extensions/test/extension_test_message_listener.h"
16 #include "net/dns/mock_host_resolver.h" 27 #include "net/dns/mock_host_resolver.h"
17 #include "net/test/embedded_test_server/embedded_test_server.h" 28 #include "net/test/embedded_test_server/embedded_test_server.h"
18 29
19 namespace extensions { 30 namespace extensions {
20 31
32 namespace {
33
34 void AddFrameToSet(std::set<content::RenderFrameHost*>* frames,
35 content::RenderFrameHost* rfh) {
36 if (rfh->IsRenderFrameLive())
37 frames->insert(rfh);
38 }
39
40 } // namespace
41
42 // Takes a snapshot of all frames upon construction. When Wait is called, a
43 // MessageLoop is created and Quit when all previously recorded frames are
44 // either present in the tab, or deleted.
45 class NavigationCompletedObserver : public content::WebContentsObserver {
46 public:
47 explicit NavigationCompletedObserver(content::WebContents* web_contents)
48 : content::WebContentsObserver(web_contents),
49 web_contents_(web_contents),
50 message_loop_runner_(new content::MessageLoopRunner) {
51 web_contents->ForEachFrame(
52 base::Bind(&AddFrameToSet, base::Unretained(&frames_)));
53 }
54
55 void Wait() {
56 if (!AreAllFramesInTab())
57 message_loop_runner_->Run();
58 }
59
60 void RenderFrameDeleted(content::RenderFrameHost* rfh) override {
61 if (frames_.erase(rfh) != 0 && message_loop_runner_->loop_running() &&
62 AreAllFramesInTab())
63 message_loop_runner_->Quit();
64 }
65
66 private:
67 bool AreAllFramesInTab() {
Devlin 2015/12/01 00:58:20 nit: please document this method.
robwu 2015/12/01 17:19:57 Done.
68 std::set<content::RenderFrameHost*> current_frames;
69 web_contents_->ForEachFrame(
70 base::Bind(&AddFrameToSet, base::Unretained(&current_frames)));
71 for (content::RenderFrameHost* frame : frames_) {
72 if (current_frames.find(frame) == current_frames.end())
73 return false;
74 }
75 return true;
76 }
77
78 content::WebContents* web_contents_;
ncarter (slow) 2015/11/30 22:56:46 You don't need this; WebContentsObserver has a web
robwu 2015/12/01 00:45:26 Done.
79 std::set<content::RenderFrameHost*> frames_;
80 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
Devlin 2015/12/01 00:58:20 nit: DISALLOW_COPY_AND_ASSIGN
robwu 2015/12/01 17:19:57 Done.
81 };
82
21 // Exists as a browser test because ExtensionHosts are hard to create without 83 // Exists as a browser test because ExtensionHosts are hard to create without
22 // a real browser. 84 // a real browser.
23 typedef ExtensionBrowserTest ProcessManagerBrowserTest; 85 class ProcessManagerBrowserTest : public ExtensionBrowserTest {
86 public:
87 // Create an extension with web-accessible frames and an optional background
88 // page.
89 const Extension* CreateExtension(const std::string& name,
90 bool has_background_process) {
91 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir);
Devlin 2015/12/01 00:58:20 nitty nit: prefer explicit construction (i.e., "ne
robwu 2015/12/01 17:19:57 Done.
92
93 DictionaryBuilder manifest;
94 manifest.Set("name", name)
95 .Set("version", "1")
96 .Set("manifest_version", 2)
97 // To allow ExecuteScript* to work.
98 .Set("content_security_policy",
99 "script-src 'self' 'unsafe-eval'; object-src 'self'")
100 .Set("web_accessible_resources", ListBuilder().Append("*"));
101
102 if (has_background_process) {
103 manifest.Set("background", DictionaryBuilder().Set("page", "bg.html"));
104 dir->WriteFile(FILE_PATH_LITERAL("bg.html"),
105 "<iframe id=bgframe src=empty.html></iframe>");
106 }
107
108 dir->WriteFile(FILE_PATH_LITERAL("blank_iframe.html"),
109 "<iframe id=frame0 src='about:blank'></iframe>");
110
111 dir->WriteFile(FILE_PATH_LITERAL("srcdoc_iframe.html"),
112 "<iframe id=frame0 srcdoc='Hello world'></iframe>");
113
114 dir->WriteFile(FILE_PATH_LITERAL("two_iframes.html"),
115 "<iframe id=frame1 src=empty.html></iframe>"
116 "<iframe id=frame2 src=empty.html></iframe>");
117
118 dir->WriteFile(FILE_PATH_LITERAL("empty.html"), "");
119
120 dir->WriteManifest(manifest.ToJSON());
121
122 const Extension* extension = LoadExtension(dir->unpacked_path());
123 EXPECT_TRUE(extension);
124 temp_dirs_.push_back(dir.Pass());
125 return extension;
126 }
127
128 // ui_test_utils::NavigateToURL sometimes returns too early: It returns as
129 // soon as the StopLoading notification has been triggered. This does not
130 // imply that RenderFrameDeleted was called, so the test may continue too
131 // early and fail when ProcessManager::GetAllFrames() returns too many frames
132 // (namely frames that are in the process of being deleted).
133 void NavigateToURL(const GURL& url) {
134 NavigationCompletedObserver observer(
135 browser()->tab_strip_model()->GetActiveWebContents());
136
137 ui_test_utils::NavigateToURL(browser(), url);
138
139 // Wait until the last RenderFrameHosts are deleted. This wait doesn't take
140 // long.
141 observer.Wait();
142 }
143
144 void NavigateIframeToURLAndWait(content::WebContents* web_contents,
145 const std::string iframe_id,
146 const GURL& url) {
147 // This is an improved version of content::NavigateIframeToURL. Unlike the
148 // other method, this does actually wait until the load of all child frames
149 // completes.
150 std::string script = base::StringPrintf(
151 "var frame = document.getElementById('%s');"
152 "frame.onload = frame.onerror = function(event) {"
153 " frame.onload = frame.onerror = null;"
154 " domAutomationController.send(event.type === 'load');"
155 "};"
156 "frame.src = '%s';",
157 iframe_id.c_str(), url.spec().c_str());
158 bool is_loaded = false;
159 EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents, script, &is_loaded));
160 EXPECT_TRUE(is_loaded);
161 }
162
163 size_t IfExtensionsIsolated(size_t if_enabled, size_t if_disabled) {
164 return content::AreAllSitesIsolatedForTesting() ||
165 IsIsolateExtensionsEnabled() ? if_enabled : if_disabled;
166 }
167
168 private:
169 std::vector<scoped_ptr<TestExtensionDir>> temp_dirs_;
Devlin 2015/12/01 00:58:20 nit: DISALLOW_COPY_AND_ASSIGN
robwu 2015/12/01 17:19:57 Done.
robwu 2015/12/01 23:19:57 I added this and compilation failed, so I've rever
170 };
24 171
25 // Test that basic extension loading creates the appropriate ExtensionHosts 172 // Test that basic extension loading creates the appropriate ExtensionHosts
26 // and background pages. 173 // and background pages.
27 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, 174 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
28 ExtensionHostCreation) { 175 ExtensionHostCreation) {
29 ProcessManager* pm = ProcessManager::Get(profile()); 176 ProcessManager* pm = ProcessManager::Get(profile());
30 177
31 // We start with no background hosts. 178 // We start with no background hosts.
32 ASSERT_EQ(0u, pm->background_hosts().size()); 179 ASSERT_EQ(0u, pm->background_hosts().size());
33 ASSERT_EQ(0u, pm->GetAllFrames().size()); 180 ASSERT_EQ(0u, pm->GetAllFrames().size());
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 content::WebContents* extension_web_contents = 297 content::WebContents* extension_web_contents =
151 content::WebContents::FromRenderFrameHost( 298 content::WebContents::FromRenderFrameHost(
152 *pm->GetRenderFrameHostsForExtension(extension->id()).begin()); 299 *pm->GetRenderFrameHostsForExtension(extension->id()).begin());
153 EXPECT_TRUE(extension_web_contents->GetSiteInstance() != 300 EXPECT_TRUE(extension_web_contents->GetSiteInstance() !=
154 tab_web_contents->GetSiteInstance()); 301 tab_web_contents->GetSiteInstance());
155 EXPECT_TRUE(pm->GetSiteInstanceForURL(extension->url()) != 302 EXPECT_TRUE(pm->GetSiteInstanceForURL(extension->url()) !=
156 tab_web_contents->GetSiteInstance()); 303 tab_web_contents->GetSiteInstance());
157 EXPECT_TRUE(pm->GetBackgroundHostForExtension(extension->id())); 304 EXPECT_TRUE(pm->GetBackgroundHostForExtension(extension->id()));
158 } 305 }
159 306
307 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, NoBackgroundPage) {
308 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
309
310 ProcessManager* pm = ProcessManager::Get(profile());
311 const Extension* extension =
312 LoadExtension(test_data_dir_.AppendASCII("api_test")
313 .AppendASCII("messaging")
Devlin 2015/12/01 00:58:20 indentation looks off - was this git cl formatted?
robwu 2015/12/01 17:19:57 No. I've now ran git cl format and the indention i
314 .AppendASCII("connect_nobackground"));
315 ASSERT_TRUE(extension);
316
317 // The extension has no background page.
318 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
319
320 // Start in a non-extension process, then navigate to an extension process.
321 NavigateToURL(embedded_test_server()->GetURL("/empty.html"));
322 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
323
324 const GURL extension_url = extension->url().Resolve("manifest.json");
325 NavigateToURL(extension_url);
326 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
327
328 NavigateToURL(GURL("about:blank"));
329 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
330
331 ui_test_utils::NavigateToURLWithDisposition(
332 browser(), extension_url, NEW_FOREGROUND_TAB,
333 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
334 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
335 }
336
337 // Tests whether frames are correctly classified. Non-extension frames should
338 // never appear in the list. Top-level extension frames should always appear.
339 // Child extension frames should only appear if it is hosted in an extension
340 // process (i.e. if the top-level frame is an extension page, or if OOP frames
341 // are enabled for extensions).
342 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, FrameClassification) {
343 const Extension* extension1 = CreateExtension("Extension 1", false);
344 const Extension* extension2 = CreateExtension("Extension 2", true);
345 embedded_test_server()->ServeFilesFromDirectory(extension1->path());
346 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
347
348 const GURL ext1_parent_url(extension1->url().Resolve("two_iframes.html"));
Devlin 2015/12/01 00:58:20 nit: for consts like these, I usually prefer kExt1
robwu 2015/12/01 17:19:57 Done.
349 const GURL ext1_empty_url(extension1->url().Resolve("empty.html"));
350 const GURL ext2_parent_url(extension2->url().Resolve("two_iframes.html"));
351 const GURL ext2_empty_url(extension2->url().Resolve("empty.html"));
352
353 ProcessManager* pm = ProcessManager::Get(profile());
354 BackgroundPageWatcher(pm, extension2).WaitForOpen();
355
356 EXPECT_EQ(2u, pm->GetAllFrames().size());
357 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
358 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
Devlin 2015/12/01 00:58:20 For this, it would probably be nice to include com
robwu 2015/12/01 17:19:57 Done.
359
360 ExecuteScriptInBackgroundPageNoWait(extension2->id(),
361 "setTimeout(window.close, 0)");
362 BackgroundPageWatcher(pm, extension2).WaitForClose();
363 EXPECT_EQ(0u, pm->GetAllFrames().size());
364 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
Devlin 2015/12/01 00:58:20 nit: unless GetAllFrames() is supremely busted, th
robwu 2015/12/01 17:19:57 In the current implementation it does not matter,
365
366 NavigateToURL(embedded_test_server()->GetURL("/two_iframes.html"));
367 EXPECT_EQ(0u, pm->GetAllFrames().size());
368
369 content::WebContents* tab =
370 browser()->tab_strip_model()->GetActiveWebContents();
371
372 // Tests extension frames in non-extension page.
373 NavigateIframeToURLAndWait(tab, "frame1", ext1_empty_url);
374 EXPECT_EQ(IfExtensionsIsolated(1, 0),
375 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
376 EXPECT_EQ(IfExtensionsIsolated(1, 0), pm->GetAllFrames().size());
377
378 NavigateIframeToURLAndWait(tab, "frame2", ext2_empty_url);
379 EXPECT_EQ(IfExtensionsIsolated(1, 0),
380 pm->GetRenderFrameHostsForExtension(extension2->id()).size());
381 EXPECT_EQ(IfExtensionsIsolated(2, 0), pm->GetAllFrames().size());
382
383 // Tests non-extension page in extension frame
384 NavigateToURL(ext1_parent_url);
Devlin 2015/12/01 00:58:20 Here, too, comments would be nice. It's hard to k
robwu 2015/12/01 17:19:57 Done.
385 EXPECT_EQ(3u, pm->GetAllFrames().size());
386 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
387 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
388
389 NavigateIframeToURLAndWait(tab, "frame1",
390 embedded_test_server()->GetURL("/empty.html"));
391 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
392 EXPECT_EQ(2u, pm->GetAllFrames().size());
393
394 NavigateIframeToURLAndWait(tab, "frame1", ext1_empty_url);
395 EXPECT_EQ(3u, pm->GetAllFrames().size());
396 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
397
398 // Load a frame from another extension.
399 NavigateIframeToURLAndWait(tab, "frame1", ext2_empty_url);
400 EXPECT_EQ(IfExtensionsIsolated(3, 2), pm->GetAllFrames().size());
401 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
402 EXPECT_EQ(IfExtensionsIsolated(1, 0),
403 pm->GetRenderFrameHostsForExtension(extension2->id()).size());
404
405 // Destroy all existing frames by navigating to another extension.
406 NavigateToURL(extension2->url().Resolve("empty.html"));
407 EXPECT_EQ(1u, pm->GetAllFrames().size());
408 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
409 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
410
411 // Test about:blank and about:srcdoc child frames.
412 NavigateToURL(extension2->url().Resolve("srcdoc_iframe.html"));
413 EXPECT_EQ(2u, pm->GetAllFrames().size());
414 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
415
416 NavigateToURL(extension2->url().Resolve("blank_iframe.html"));
417 EXPECT_EQ(2u, pm->GetAllFrames().size());
418 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
419
420 // Test nested frames (same extension).
421 NavigateToURL(ext2_parent_url);
422 EXPECT_EQ(3u, pm->GetAllFrames().size());
423 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
424
425 NavigateIframeToURLAndWait(tab, "frame1", ext2_parent_url);
426 EXPECT_EQ(5u, pm->GetAllFrames().size());
427 EXPECT_EQ(5u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
428
429 // The extension frame from the other extension should not be classified as an
430 // extension (unless out-of-process frames are enabled).
431 NavigateIframeToURLAndWait(tab, "frame1", ext1_empty_url);
432 EXPECT_EQ(IfExtensionsIsolated(3, 2), pm->GetAllFrames().size());
433 EXPECT_EQ(IfExtensionsIsolated(1, 0),
434 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
435 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
436
437 NavigateIframeToURLAndWait(tab, "frame2", ext1_parent_url);
438 EXPECT_EQ(IfExtensionsIsolated(5, 1), pm->GetAllFrames().size());
439 EXPECT_EQ(IfExtensionsIsolated(4, 0),
440 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
441 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
442
443 // Crash tab where the top-level frame is an extension frame.
444 content::CrashTab(tab);
445 EXPECT_EQ(0u, pm->GetAllFrames().size());
446 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
447 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
448
449 // Now load an extension page and a non-extension page...
450 ui_test_utils::NavigateToURLWithDisposition(
451 browser(), ext1_empty_url, NEW_BACKGROUND_TAB,
452 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
453 NavigateToURL(embedded_test_server()->GetURL("/two_iframes.html"));
454 EXPECT_EQ(1u, pm->GetAllFrames().size());
455
456 // ... load an extension frame in the non-extension process
457 NavigateIframeToURLAndWait(tab, "frame1", ext1_empty_url);
458 EXPECT_EQ(IfExtensionsIsolated(2, 1),
459 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
460
461 // ... and take down the tab. The extension process is not part of the tab,
462 // so it should be kept alive (minus the frames that died).
463 content::CrashTab(tab);
464 EXPECT_EQ(1u, pm->GetAllFrames().size());
465 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
466 }
467
160 // Verify correct keepalive count behavior on network request events. 468 // Verify correct keepalive count behavior on network request events.
161 // Regression test for http://crbug.com/535716. 469 // Regression test for http://crbug.com/535716.
162 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, KeepaliveOnNetworkRequest) { 470 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, KeepaliveOnNetworkRequest) {
163 // Load an extension with a lazy background page. 471 // Load an extension with a lazy background page.
164 scoped_refptr<const Extension> extension = 472 scoped_refptr<const Extension> extension =
165 LoadExtension(test_data_dir_.AppendASCII("api_test") 473 LoadExtension(test_data_dir_.AppendASCII("api_test")
166 .AppendASCII("lazy_background_page") 474 .AppendASCII("lazy_background_page")
167 .AppendASCII("broadcast_event")); 475 .AppendASCII("broadcast_event"));
168 ASSERT_TRUE(extension.get()); 476 ASSERT_TRUE(extension.get());
169 477
(...skipping 17 matching lines...) Expand all
187 pm->OnNetworkRequestDone(frame_host, 1); 495 pm->OnNetworkRequestDone(frame_host, 1);
188 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get())); 496 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get()));
189 497
190 // Simulate only a request completion for this ID and ensure it doesn't result 498 // Simulate only a request completion for this ID and ensure it doesn't result
191 // in keepalive decrement. 499 // in keepalive decrement.
192 pm->OnNetworkRequestDone(frame_host, 2); 500 pm->OnNetworkRequestDone(frame_host, 2);
193 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get())); 501 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get()));
194 } 502 }
195 503
196 } // namespace extensions 504 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698