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

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: Cleanup + remove DCHECK 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. If a navigation happens between the
45 // construction and the Wait() call, then this logic ensures that all obsolete
46 // RenderFrameHosts have been destructed when Wait() returns.
47 // See also the comment at ProcessManagerBrowserTest::NavigateToURL.
48 class NavigationCompletedObserver : public content::WebContentsObserver {
49 public:
50 explicit NavigationCompletedObserver(content::WebContents* web_contents)
51 : content::WebContentsObserver(web_contents),
52 message_loop_runner_(new content::MessageLoopRunner) {
53 web_contents->ForEachFrame(
54 base::Bind(&AddFrameToSet, base::Unretained(&frames_)));
55 }
56
57 void Wait() {
58 if (!AreAllFramesInTab())
59 message_loop_runner_->Run();
60 }
61
62 void RenderFrameDeleted(content::RenderFrameHost* rfh) override {
63 if (frames_.erase(rfh) != 0 && message_loop_runner_->loop_running() &&
64 AreAllFramesInTab())
65 message_loop_runner_->Quit();
66 }
67
68 private:
69 // Check whether all frames that were recorded at the construction of this
70 // class are still part of the tab.
71 bool AreAllFramesInTab() {
72 std::set<content::RenderFrameHost*> current_frames;
73 web_contents()->ForEachFrame(
74 base::Bind(&AddFrameToSet, base::Unretained(&current_frames)));
75 for (content::RenderFrameHost* frame : frames_) {
76 if (current_frames.find(frame) == current_frames.end())
77 return false;
78 }
79 return true;
80 }
81
82 std::set<content::RenderFrameHost*> frames_;
83 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
84
85 DISALLOW_COPY_AND_ASSIGN(NavigationCompletedObserver);
86 };
87
21 // Exists as a browser test because ExtensionHosts are hard to create without 88 // Exists as a browser test because ExtensionHosts are hard to create without
22 // a real browser. 89 // a real browser.
23 typedef ExtensionBrowserTest ProcessManagerBrowserTest; 90 class ProcessManagerBrowserTest : public ExtensionBrowserTest {
91 public:
92 // Create an extension with web-accessible frames and an optional background
93 // page.
94 const Extension* CreateExtension(const std::string& name,
95 bool has_background_process) {
96 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir());
97
98 DictionaryBuilder manifest;
99 manifest.Set("name", name)
100 .Set("version", "1")
101 .Set("manifest_version", 2)
102 // To allow ExecuteScript* to work.
103 .Set("content_security_policy",
104 "script-src 'self' 'unsafe-eval'; object-src 'self'")
105 .Set("sandbox", DictionaryBuilder().Set(
106 "pages", ListBuilder().Append("sandboxed.html")))
107 .Set("web_accessible_resources", ListBuilder().Append("*"));
108
109 if (has_background_process) {
110 manifest.Set("background", DictionaryBuilder().Set("page", "bg.html"));
111 dir->WriteFile(FILE_PATH_LITERAL("bg.html"),
112 "<iframe id='bgframe' src='empty.html'></iframe>");
113 }
114
115 dir->WriteFile(FILE_PATH_LITERAL("blank_iframe.html"),
116 "<iframe id='frame0' src='about:blank'></iframe>");
117
118 dir->WriteFile(FILE_PATH_LITERAL("srcdoc_iframe.html"),
119 "<iframe id='frame0' srcdoc='Hello world'></iframe>");
120
121 dir->WriteFile(FILE_PATH_LITERAL("two_iframes.html"),
122 "<iframe id='frame1' src='empty.html'></iframe>"
123 "<iframe id='frame2' src='empty.html'></iframe>");
124
125 dir->WriteFile(FILE_PATH_LITERAL("sandboxed.html"), "Some sandboxed page");
126
127 dir->WriteFile(FILE_PATH_LITERAL("empty.html"), "");
128
129 dir->WriteManifest(manifest.ToJSON());
130
131 const Extension* extension = LoadExtension(dir->unpacked_path());
132 EXPECT_TRUE(extension);
133 temp_dirs_.push_back(dir.Pass());
134 return extension;
135 }
136
137 // ui_test_utils::NavigateToURL sometimes returns too early: It returns as
138 // soon as the StopLoading notification has been triggered. This does not
139 // imply that RenderFrameDeleted was called, so the test may continue too
140 // early and fail when ProcessManager::GetAllFrames() returns too many frames
141 // (namely frames that are in the process of being deleted). To work around
142 // this problem, we also wait until all previous frames have been deleted.
143 void NavigateToURL(const GURL& url) {
144 NavigationCompletedObserver observer(
145 browser()->tab_strip_model()->GetActiveWebContents());
146
147 ui_test_utils::NavigateToURL(browser(), url);
148
149 // Wait until the last RenderFrameHosts are deleted. This wait doesn't take
150 // long.
151 observer.Wait();
152 }
153
154 void NavigateIframeToURLAndWait(content::WebContents* web_contents,
155 const std::string iframe_id,
156 const GURL& url) {
157 // This is an improved version of content::NavigateIframeToURL. Unlike the
158 // other method, this does actually wait until the load of all child frames
159 // completes.
160 std::string script = base::StringPrintf(
161 "var frame = document.getElementById('%s');"
162 "frame.onload = frame.onerror = function(event) {"
163 " frame.onload = frame.onerror = null;"
164 " domAutomationController.send(event.type === 'load');"
165 "};"
166 "frame.src = '%s';",
167 iframe_id.c_str(), url.spec().c_str());
168 bool is_loaded = false;
169 EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents, script, &is_loaded));
170 EXPECT_TRUE(is_loaded);
171 }
172
173 size_t IfExtensionsIsolated(size_t if_enabled, size_t if_disabled) {
174 return content::AreAllSitesIsolatedForTesting() ||
175 IsIsolateExtensionsEnabled()
176 ? if_enabled
177 : if_disabled;
178 }
179
180 private:
181 std::vector<scoped_ptr<TestExtensionDir>> temp_dirs_;
182 };
24 183
25 // Test that basic extension loading creates the appropriate ExtensionHosts 184 // Test that basic extension loading creates the appropriate ExtensionHosts
26 // and background pages. 185 // and background pages.
27 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, 186 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
28 ExtensionHostCreation) { 187 ExtensionHostCreation) {
29 ProcessManager* pm = ProcessManager::Get(profile()); 188 ProcessManager* pm = ProcessManager::Get(profile());
30 189
31 // We start with no background hosts. 190 // We start with no background hosts.
32 ASSERT_EQ(0u, pm->background_hosts().size()); 191 ASSERT_EQ(0u, pm->background_hosts().size());
33 ASSERT_EQ(0u, pm->GetAllFrames().size()); 192 ASSERT_EQ(0u, pm->GetAllFrames().size());
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 content::WebContents* extension_web_contents = 309 content::WebContents* extension_web_contents =
151 content::WebContents::FromRenderFrameHost( 310 content::WebContents::FromRenderFrameHost(
152 *pm->GetRenderFrameHostsForExtension(extension->id()).begin()); 311 *pm->GetRenderFrameHostsForExtension(extension->id()).begin());
153 EXPECT_TRUE(extension_web_contents->GetSiteInstance() != 312 EXPECT_TRUE(extension_web_contents->GetSiteInstance() !=
154 tab_web_contents->GetSiteInstance()); 313 tab_web_contents->GetSiteInstance());
155 EXPECT_TRUE(pm->GetSiteInstanceForURL(extension->url()) != 314 EXPECT_TRUE(pm->GetSiteInstanceForURL(extension->url()) !=
156 tab_web_contents->GetSiteInstance()); 315 tab_web_contents->GetSiteInstance());
157 EXPECT_TRUE(pm->GetBackgroundHostForExtension(extension->id())); 316 EXPECT_TRUE(pm->GetBackgroundHostForExtension(extension->id()));
158 } 317 }
159 318
319 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, NoBackgroundPage) {
320 ASSERT_TRUE(embedded_test_server()->Start());
321
322 ProcessManager* pm = ProcessManager::Get(profile());
323 const Extension* extension =
324 LoadExtension(test_data_dir_.AppendASCII("api_test")
325 .AppendASCII("messaging")
326 .AppendASCII("connect_nobackground"));
327 ASSERT_TRUE(extension);
328
329 // The extension has no background page.
330 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
331
332 // Start in a non-extension process, then navigate to an extension process.
333 NavigateToURL(embedded_test_server()->GetURL("/empty.html"));
334 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
335
336 const GURL extension_url = extension->url().Resolve("manifest.json");
337 NavigateToURL(extension_url);
338 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
339
340 NavigateToURL(GURL("about:blank"));
341 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
342
343 ui_test_utils::NavigateToURLWithDisposition(
344 browser(), extension_url, NEW_FOREGROUND_TAB,
345 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
346 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
347 }
348
349 // Tests whether frames are correctly classified. Non-extension frames should
350 // never appear in the list. Top-level extension frames should always appear.
351 // Child extension frames should only appear if it is hosted in an extension
352 // process (i.e. if the top-level frame is an extension page, or if OOP frames
353 // are enabled for extensions).
354 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, FrameClassification) {
355 const Extension* extension1 = CreateExtension("Extension 1", false);
356 const Extension* extension2 = CreateExtension("Extension 2", true);
357 embedded_test_server()->ServeFilesFromDirectory(extension1->path());
358 ASSERT_TRUE(embedded_test_server()->Start());
359
360 const GURL kExt1TwoFramesUrl(extension1->url().Resolve("two_iframes.html"));
361 const GURL kExt1EmptyUrl(extension1->url().Resolve("empty.html"));
362 const GURL kExt2TwoFramesUrl(extension2->url().Resolve("two_iframes.html"));
363 const GURL kExt2EmptyUrl(extension2->url().Resolve("empty.html"));
364
365 ProcessManager* pm = ProcessManager::Get(profile());
366
367 // 1 background page + 1 frame in background page from Extension 2.
368 BackgroundPageWatcher(pm, extension2).WaitForOpen();
369 EXPECT_EQ(2u, pm->GetAllFrames().size());
370 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
371 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
372
373 ExecuteScriptInBackgroundPageNoWait(extension2->id(),
374 "setTimeout(window.close, 0)");
375 BackgroundPageWatcher(pm, extension2).WaitForClose();
376 EXPECT_EQ(0u, pm->GetAllFrames().size());
377 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
378
379 NavigateToURL(embedded_test_server()->GetURL("/two_iframes.html"));
380 EXPECT_EQ(0u, pm->GetAllFrames().size());
381
382 content::WebContents* tab =
383 browser()->tab_strip_model()->GetActiveWebContents();
384
385 // Tests extension frames in non-extension page.
386 NavigateIframeToURLAndWait(tab, "frame1", kExt1EmptyUrl);
387 EXPECT_EQ(IfExtensionsIsolated(1, 0),
388 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
389 EXPECT_EQ(IfExtensionsIsolated(1, 0), pm->GetAllFrames().size());
390
391 NavigateIframeToURLAndWait(tab, "frame2", kExt2EmptyUrl);
392 EXPECT_EQ(IfExtensionsIsolated(1, 0),
393 pm->GetRenderFrameHostsForExtension(extension2->id()).size());
394 EXPECT_EQ(IfExtensionsIsolated(2, 0), pm->GetAllFrames().size());
395
396 // Tests non-extension page in extension frame.
397 NavigateToURL(kExt1TwoFramesUrl);
398 // 1 top-level + 2 child frames from Extension 1.
399 EXPECT_EQ(3u, pm->GetAllFrames().size());
400 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
401 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
402
403 NavigateIframeToURLAndWait(tab, "frame1",
404 embedded_test_server()->GetURL("/empty.html"));
405 // 1 top-level + 1 child frame from Extension 1.
406 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
407 EXPECT_EQ(2u, pm->GetAllFrames().size());
408
409 NavigateIframeToURLAndWait(tab, "frame1", kExt1EmptyUrl);
410 // 1 top-level + 2 child frames from Extension 1.
411 EXPECT_EQ(3u, pm->GetAllFrames().size());
412 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
413
414 // Load a frame from another extension.
415 NavigateIframeToURLAndWait(tab, "frame1", kExt2EmptyUrl);
416 // 1 top-level + 1 child frame from Extension 1,
417 // 1 child frame from Extension 2.
418 EXPECT_EQ(IfExtensionsIsolated(3, 2), pm->GetAllFrames().size());
419 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
420 EXPECT_EQ(IfExtensionsIsolated(1, 0),
421 pm->GetRenderFrameHostsForExtension(extension2->id()).size());
422
423 // Destroy all existing frames by navigating to another extension.
424 NavigateToURL(extension2->url().Resolve("empty.html"));
425 EXPECT_EQ(1u, pm->GetAllFrames().size());
426 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
427 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
428
429 // Test about:blank and about:srcdoc child frames.
430 NavigateToURL(extension2->url().Resolve("srcdoc_iframe.html"));
431 // 1 top-level frame + 1 child frame from Extension 2.
432 EXPECT_EQ(2u, pm->GetAllFrames().size());
433 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
434
435 NavigateToURL(extension2->url().Resolve("blank_iframe.html"));
436 // 1 top-level frame + 1 child frame from Extension 2.
437 EXPECT_EQ(2u, pm->GetAllFrames().size());
438 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
439
440 // Sandboxed frames are not viewed as extension frames.
441 NavigateIframeToURLAndWait(tab, "frame0",
442 extension2->url().Resolve("sandboxed.html"));
443 // 1 top-level frame from Extension 2.
444 EXPECT_EQ(1u, pm->GetAllFrames().size());
445 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
446
447 NavigateToURL(extension2->url().Resolve("sandboxed.html"));
448 EXPECT_EQ(0u, pm->GetAllFrames().size());
449 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
450
451 // Test nested frames (same extension).
452 NavigateToURL(kExt2TwoFramesUrl);
453 // 1 top-level + 2 child frames from Extension 2.
454 EXPECT_EQ(3u, pm->GetAllFrames().size());
455 EXPECT_EQ(3u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
456
457 NavigateIframeToURLAndWait(tab, "frame1", kExt2TwoFramesUrl);
458 // 1 top-level + 2 child frames from Extension 1,
459 // 2 child frames in frame1 from Extension 2.
460 EXPECT_EQ(5u, pm->GetAllFrames().size());
461 EXPECT_EQ(5u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
462
463 // The extension frame from the other extension should not be classified as an
464 // extension (unless out-of-process frames are enabled).
465 NavigateIframeToURLAndWait(tab, "frame1", kExt1EmptyUrl);
466 // 1 top-level + 1 child frames from Extension 2,
467 // 1 child frame from Extension 1.
468 EXPECT_EQ(IfExtensionsIsolated(3, 2), pm->GetAllFrames().size());
469 EXPECT_EQ(IfExtensionsIsolated(1, 0),
470 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
471 EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
472
473 NavigateIframeToURLAndWait(tab, "frame2", kExt1TwoFramesUrl);
474 // 1 top-level + 1 child frames from Extension 2,
475 // 1 child frame + 2 child frames in frame2 from Extension 1.
476 EXPECT_EQ(IfExtensionsIsolated(5, 1), pm->GetAllFrames().size());
477 EXPECT_EQ(IfExtensionsIsolated(4, 0),
478 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
479 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
480
481 // Crash tab where the top-level frame is an extension frame.
482 content::CrashTab(tab);
483 EXPECT_EQ(0u, pm->GetAllFrames().size());
484 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
485 EXPECT_EQ(0u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
486
487 // Now load an extension page and a non-extension page...
488 ui_test_utils::NavigateToURLWithDisposition(
489 browser(), kExt1EmptyUrl, NEW_BACKGROUND_TAB,
490 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
491 NavigateToURL(embedded_test_server()->GetURL("/two_iframes.html"));
492 EXPECT_EQ(1u, pm->GetAllFrames().size());
493
494 // ... load an extension frame in the non-extension process
495 NavigateIframeToURLAndWait(tab, "frame1", kExt1EmptyUrl);
496 EXPECT_EQ(IfExtensionsIsolated(2, 1),
497 pm->GetRenderFrameHostsForExtension(extension1->id()).size());
498
499 // ... and take down the tab. The extension process is not part of the tab,
500 // so it should be kept alive (minus the frames that died).
501 content::CrashTab(tab);
502 EXPECT_EQ(1u, pm->GetAllFrames().size());
503 EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
504 }
505
160 // Verify correct keepalive count behavior on network request events. 506 // Verify correct keepalive count behavior on network request events.
161 // Regression test for http://crbug.com/535716. 507 // Regression test for http://crbug.com/535716.
162 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, KeepaliveOnNetworkRequest) { 508 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest, KeepaliveOnNetworkRequest) {
163 // Load an extension with a lazy background page. 509 // Load an extension with a lazy background page.
164 scoped_refptr<const Extension> extension = 510 scoped_refptr<const Extension> extension =
165 LoadExtension(test_data_dir_.AppendASCII("api_test") 511 LoadExtension(test_data_dir_.AppendASCII("api_test")
166 .AppendASCII("lazy_background_page") 512 .AppendASCII("lazy_background_page")
167 .AppendASCII("broadcast_event")); 513 .AppendASCII("broadcast_event"));
168 ASSERT_TRUE(extension.get()); 514 ASSERT_TRUE(extension.get());
169 515
(...skipping 17 matching lines...) Expand all
187 pm->OnNetworkRequestDone(frame_host, 1); 533 pm->OnNetworkRequestDone(frame_host, 1);
188 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get())); 534 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get()));
189 535
190 // Simulate only a request completion for this ID and ensure it doesn't result 536 // Simulate only a request completion for this ID and ensure it doesn't result
191 // in keepalive decrement. 537 // in keepalive decrement.
192 pm->OnNetworkRequestDone(frame_host, 2); 538 pm->OnNetworkRequestDone(frame_host, 2);
193 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get())); 539 EXPECT_EQ(baseline_keepalive, pm->GetLazyKeepaliveCount(extension.get()));
194 } 540 }
195 541
196 } // namespace extensions 542 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698