web_contents_impl_browsertest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
3// found in the LICENSE file.
4
5#include "base/strings/utf_string_conversions.h"
6#include "base/values.h"
7#include "content/browser/frame_host/navigation_entry_impl.h"
8#include "content/browser/web_contents/web_contents_impl.h"
9#include "content/browser/web_contents/web_contents_view.h"
10#include "content/public/browser/load_notification_details.h"
11#include "content/public/browser/navigation_controller.h"
12#include "content/public/browser/notification_details.h"
13#include "content/public/browser/notification_observer.h"
14#include "content/public/browser/notification_types.h"
15#include "content/public/browser/render_view_host.h"
16#include "content/public/browser/render_widget_host_view.h"
17#include "content/public/browser/web_contents_observer.h"
18#include "content/public/common/content_paths.h"
19#include "content/public/test/browser_test_utils.h"
20#include "content/public/test/content_browser_test.h"
21#include "content/public/test/content_browser_test_utils.h"
22#include "content/public/test/test_utils.h"
23#include "content/shell/browser/shell.h"
24#include "net/dns/mock_host_resolver.h"
25#include "net/test/embedded_test_server/embedded_test_server.h"
26
27namespace content {
28
29void ResizeWebContentsView(Shell* shell, const gfx::Size& size,
30                           bool set_start_page) {
31  // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents
32  // works on Win and ChromeOS but not Linux - we need to resize the shell
33  // window on Linux because if we don't, the next layout of the unchanged shell
34  // window will resize WebContentsView back to the previous size.
35  // SizeContents is a hack and should not be relied on.
36#if defined(OS_MACOSX)
37  shell->SizeTo(size);
38  // If |set_start_page| is true, start with blank page to make sure resize
39  // takes effect.
40  if (set_start_page)
41    NavigateToURL(shell, GURL("about://blank"));
42#else
43  static_cast<WebContentsImpl*>(shell->web_contents())->GetView()->
44      SizeContents(size);
45#endif  // defined(OS_MACOSX)
46}
47
48class WebContentsImplBrowserTest : public ContentBrowserTest {
49 public:
50  WebContentsImplBrowserTest() {}
51
52 private:
53  DISALLOW_COPY_AND_ASSIGN(WebContentsImplBrowserTest);
54};
55
56// Keeps track of data from LoadNotificationDetails so we can later verify that
57// they are correct, after the LoadNotificationDetails object is deleted.
58class LoadStopNotificationObserver : public WindowedNotificationObserver {
59 public:
60  LoadStopNotificationObserver(NavigationController* controller)
61      : WindowedNotificationObserver(NOTIFICATION_LOAD_STOP,
62                                     Source<NavigationController>(controller)),
63        session_index_(-1),
64        controller_(NULL) {
65  }
66  virtual void Observe(int type,
67                       const NotificationSource& source,
68                       const NotificationDetails& details) OVERRIDE {
69    if (type == NOTIFICATION_LOAD_STOP) {
70      const Details<LoadNotificationDetails> load_details(details);
71      url_ = load_details->url;
72      session_index_ = load_details->session_index;
73      controller_ = load_details->controller;
74    }
75    WindowedNotificationObserver::Observe(type, source, details);
76  }
77
78  GURL url_;
79  int session_index_;
80  NavigationController* controller_;
81};
82
83// Starts a new navigation as soon as the current one commits, but does not
84// wait for it to complete.  This allows us to observe DidStopLoading while
85// a pending entry is present.
86class NavigateOnCommitObserver : public WebContentsObserver {
87 public:
88  NavigateOnCommitObserver(Shell* shell, GURL url)
89      : WebContentsObserver(shell->web_contents()),
90        shell_(shell),
91        url_(url),
92        done_(false) {
93  }
94
95  // WebContentsObserver:
96  virtual void NavigationEntryCommitted(
97      const LoadCommittedDetails& load_details) OVERRIDE {
98    if (!done_) {
99      done_ = true;
100      shell_->Stop();
101      shell_->LoadURL(url_);
102    }
103  }
104
105  Shell* shell_;
106  GURL url_;
107  bool done_;
108};
109
110class RenderViewSizeDelegate : public WebContentsDelegate {
111 public:
112  void set_size_insets(const gfx::Size& size_insets) {
113    size_insets_ = size_insets;
114  }
115
116  // WebContentsDelegate:
117  virtual gfx::Size GetSizeForNewRenderView(
118      WebContents* web_contents) const OVERRIDE {
119    gfx::Size size(web_contents->GetContainerBounds().size());
120    size.Enlarge(size_insets_.width(), size_insets_.height());
121    return size;
122  }
123
124 private:
125  gfx::Size size_insets_;
126};
127
128class RenderViewSizeObserver : public WebContentsObserver {
129 public:
130  RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size)
131      : WebContentsObserver(shell->web_contents()),
132        shell_(shell),
133        wcv_new_size_(wcv_new_size) {
134  }
135
136  // WebContentsObserver:
137  virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE {
138    rwhv_create_size_ = rvh->GetView()->GetViewBounds().size();
139  }
140
141  virtual void DidStartNavigationToPendingEntry(
142      const GURL& url,
143      NavigationController::ReloadType reload_type) OVERRIDE {
144    ResizeWebContentsView(shell_, wcv_new_size_, false);
145  }
146
147  gfx::Size rwhv_create_size() const { return rwhv_create_size_; }
148
149 private:
150  Shell* shell_;  // Weak ptr.
151  gfx::Size wcv_new_size_;
152  gfx::Size rwhv_create_size_;
153};
154
155class LoadingStateChangedDelegate : public WebContentsDelegate {
156 public:
157  LoadingStateChangedDelegate()
158      : loadingStateChangedCount_(0)
159      , loadingStateToDifferentDocumentCount_(0) {
160  }
161
162  // WebContentsDelegate:
163  virtual void LoadingStateChanged(WebContents* contents,
164                                   bool to_different_document) OVERRIDE {
165      loadingStateChangedCount_++;
166      if (to_different_document)
167        loadingStateToDifferentDocumentCount_++;
168  }
169
170  int loadingStateChangedCount() const { return loadingStateChangedCount_; }
171  int loadingStateToDifferentDocumentCount() const {
172    return loadingStateToDifferentDocumentCount_;
173  }
174
175 private:
176  int loadingStateChangedCount_;
177  int loadingStateToDifferentDocumentCount_;
178};
179
180// See: http://crbug.com/298193
181#if defined(OS_WIN)
182#define MAYBE_DidStopLoadingDetails DISABLED_DidStopLoadingDetails
183#else
184#define MAYBE_DidStopLoadingDetails DidStopLoadingDetails
185#endif
186
187// Test that DidStopLoading includes the correct URL in the details.
188IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
189                       MAYBE_DidStopLoadingDetails) {
190  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
191
192  LoadStopNotificationObserver load_observer(
193      &shell()->web_contents()->GetController());
194  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
195  load_observer.Wait();
196
197  EXPECT_EQ("/title1.html", load_observer.url_.path());
198  EXPECT_EQ(0, load_observer.session_index_);
199  EXPECT_EQ(&shell()->web_contents()->GetController(),
200            load_observer.controller_);
201}
202
203// See: http://crbug.com/298193
204#if defined(OS_WIN)
205#define MAYBE_DidStopLoadingDetailsWithPending \
206  DISABLED_DidStopLoadingDetailsWithPending
207#else
208#define MAYBE_DidStopLoadingDetailsWithPending DidStopLoadingDetailsWithPending
209#endif
210
211// Test that DidStopLoading includes the correct URL in the details when a
212// pending entry is present.
213IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
214                       MAYBE_DidStopLoadingDetailsWithPending) {
215  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
216  GURL url("data:text/html,<div>test</div>");
217
218  // Listen for the first load to stop.
219  LoadStopNotificationObserver load_observer(
220      &shell()->web_contents()->GetController());
221  // Start a new pending navigation as soon as the first load commits.
222  // We will hear a DidStopLoading from the first load as the new load
223  // is started.
224  NavigateOnCommitObserver commit_observer(
225      shell(), embedded_test_server()->GetURL("/title2.html"));
226  NavigateToURL(shell(), url);
227  load_observer.Wait();
228
229  EXPECT_EQ(url, load_observer.url_);
230  EXPECT_EQ(0, load_observer.session_index_);
231  EXPECT_EQ(&shell()->web_contents()->GetController(),
232            load_observer.controller_);
233}
234// Test that a renderer-initiated navigation to an invalid URL does not leave
235// around a pending entry that could be used in a URL spoof.  We test this in
236// a browser test because our unit test framework incorrectly calls
237// DidStartProvisionalLoadForFrame for in-page navigations.
238// See http://crbug.com/280512.
239IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
240                       ClearNonVisiblePendingOnFail) {
241  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
242
243  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
244
245  // Navigate to an invalid URL and make sure it doesn't leave a pending entry.
246  LoadStopNotificationObserver load_observer1(
247      &shell()->web_contents()->GetController());
248  ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
249                            "window.location.href=\"nonexistent:12121\";"));
250  load_observer1.Wait();
251  EXPECT_FALSE(shell()->web_contents()->GetController().GetPendingEntry());
252
253  LoadStopNotificationObserver load_observer2(
254      &shell()->web_contents()->GetController());
255  ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
256                            "window.location.href=\"#foo\";"));
257  load_observer2.Wait();
258  EXPECT_EQ(embedded_test_server()->GetURL("/title1.html#foo"),
259            shell()->web_contents()->GetVisibleURL());
260}
261
262// Crashes under ThreadSanitizer, http://crbug.com/356758.
263#if defined(OS_WIN) || defined(OS_ANDROID) \
264    || defined(THREAD_SANITIZER)
265#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
266#else
267#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
268#endif
269// Test that RenderViewHost is created and updated at the size specified by
270// WebContentsDelegate::GetSizeForNewRenderView().
271IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
272                       MAYBE_GetSizeForNewRenderView) {
273  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
274  // Create a new server with a different site.
275  net::SpawnedTestServer https_server(
276      net::SpawnedTestServer::TYPE_HTTPS,
277      net::SpawnedTestServer::kLocalhost,
278      base::FilePath(FILE_PATH_LITERAL("content/test/data")));
279  ASSERT_TRUE(https_server.Start());
280
281  scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate());
282  shell()->web_contents()->SetDelegate(delegate.get());
283  ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
284
285  // When no size is set, RenderWidgetHostView adopts the size of
286  // WebContentsView.
287  NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"));
288  EXPECT_EQ(shell()->web_contents()->GetContainerBounds().size(),
289            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
290                size());
291
292  // When a size is set, RenderWidgetHostView and WebContentsView honor this
293  // size.
294  gfx::Size size(300, 300);
295  gfx::Size size_insets(10, 15);
296  ResizeWebContentsView(shell(), size, true);
297  delegate->set_size_insets(size_insets);
298  NavigateToURL(shell(), https_server.GetURL("/"));
299  size.Enlarge(size_insets.width(), size_insets.height());
300  EXPECT_EQ(size,
301            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
302                size());
303  // The web_contents size is set by the embedder, and should not depend on the
304  // rwhv size. The behavior is correct on OSX, but incorrect on other
305  // platforms.
306  gfx::Size exp_wcv_size(300, 300);
307#if !defined(OS_MACOSX)
308  exp_wcv_size.Enlarge(size_insets.width(), size_insets.height());
309#endif
310
311  EXPECT_EQ(exp_wcv_size,
312            shell()->web_contents()->GetContainerBounds().size());
313
314  // If WebContentsView is resized after RenderWidgetHostView is created but
315  // before pending navigation entry is committed, both RenderWidgetHostView and
316  // WebContentsView use the new size of WebContentsView.
317  gfx::Size init_size(200, 200);
318  gfx::Size new_size(100, 100);
319  size_insets = gfx::Size(20, 30);
320  ResizeWebContentsView(shell(), init_size, true);
321  delegate->set_size_insets(size_insets);
322  RenderViewSizeObserver observer(shell(), new_size);
323  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
324  // RenderWidgetHostView is created at specified size.
325  init_size.Enlarge(size_insets.width(), size_insets.height());
326  EXPECT_EQ(init_size, observer.rwhv_create_size());
327
328// Once again, the behavior is correct on OSX. The embedder explicitly sets
329// the size to (100,100) during navigation. Both the wcv and the rwhv should
330// take on that size.
331#if !defined(OS_MACOSX)
332  new_size.Enlarge(size_insets.width(), size_insets.height());
333#endif
334  gfx::Size actual_size = shell()->web_contents()->GetRenderWidgetHostView()->
335      GetViewBounds().size();
336
337  EXPECT_EQ(new_size, actual_size);
338  EXPECT_EQ(new_size, shell()->web_contents()->GetContainerBounds().size());
339}
340
341IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, OpenURLSubframe) {
342  // Navigate to a page with frames and grab a subframe's FrameTreeNode ID.
343  ASSERT_TRUE(test_server()->Start());
344  NavigateToURL(shell(),
345                test_server()->GetURL("files/frame_tree/top.html"));
346  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
347  FrameTreeNode* root = wc->GetFrameTree()->root();
348  ASSERT_EQ(3UL, root->child_count());
349  int64 frame_tree_node_id = root->child_at(0)->frame_tree_node_id();
350  EXPECT_NE(-1, frame_tree_node_id);
351
352  // Navigate with the subframe's FrameTreeNode ID.
353  const GURL url(test_server()->GetURL("files/title1.html"));
354  OpenURLParams params(url, Referrer(), frame_tree_node_id, CURRENT_TAB,
355                       ui::PAGE_TRANSITION_LINK, true);
356  shell()->web_contents()->OpenURL(params);
357
358  // Make sure the NavigationEntry ends up with the FrameTreeNode ID.
359  NavigationController* controller = &shell()->web_contents()->GetController();
360  EXPECT_TRUE(controller->GetPendingEntry());
361  EXPECT_EQ(frame_tree_node_id,
362            NavigationEntryImpl::FromNavigationEntry(
363                controller->GetPendingEntry())->frame_tree_node_id());
364}
365
366// Observer class to track the creation of RenderFrameHost objects. It is used
367// in subsequent tests.
368class RenderFrameCreatedObserver : public WebContentsObserver {
369 public:
370  RenderFrameCreatedObserver(Shell* shell)
371      : WebContentsObserver(shell->web_contents()),
372        last_rfh_(NULL) {
373  }
374
375  virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
376    last_rfh_ = render_frame_host;
377  }
378
379  RenderFrameHost* last_rfh() const { return last_rfh_; }
380
381 private:
382  RenderFrameHost* last_rfh_;
383
384  DISALLOW_COPY_AND_ASSIGN(RenderFrameCreatedObserver);
385};
386
387// Test that creation of new RenderFrameHost objects sends the correct object
388// to the WebContentObservers. See http://crbug.com/347339.
389IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
390                       RenderFrameCreatedCorrectProcessForObservers) {
391  std::string foo_com("foo.com");
392  GURL::Replacements replace_host;
393  net::HostPortPair foo_host_port;
394  GURL cross_site_url;
395
396  // Setup the server to allow serving separate sites, so we can perform
397  // cross-process navigation.
398  host_resolver()->AddRule("*", "127.0.0.1");
399  ASSERT_TRUE(test_server()->Start());
400
401  foo_host_port = test_server()->host_port_pair();
402  foo_host_port.set_host(foo_com);
403
404  GURL initial_url(test_server()->GetURL("/title1.html"));
405
406  cross_site_url = test_server()->GetURL("/title2.html");
407  replace_host.SetHostStr(foo_com);
408  cross_site_url = cross_site_url.ReplaceComponents(replace_host);
409
410  // Navigate to the initial URL and capture the RenderFrameHost for later
411  // comparison.
412  NavigateToURL(shell(), initial_url);
413  RenderFrameHost* orig_rfh = shell()->web_contents()->GetMainFrame();
414
415  // Install the observer and navigate cross-site.
416  RenderFrameCreatedObserver observer(shell());
417  NavigateToURL(shell(), cross_site_url);
418
419  // The observer should've seen a RenderFrameCreated call for the new frame
420  // and not the old one.
421  EXPECT_NE(observer.last_rfh(), orig_rfh);
422  EXPECT_EQ(observer.last_rfh(), shell()->web_contents()->GetMainFrame());
423}
424
425IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
426                       LoadingStateChangedForSameDocumentNavigation) {
427  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
428  scoped_ptr<LoadingStateChangedDelegate> delegate(
429      new LoadingStateChangedDelegate());
430  shell()->web_contents()->SetDelegate(delegate.get());
431
432  LoadStopNotificationObserver load_observer(
433      &shell()->web_contents()->GetController());
434  TitleWatcher title_watcher(shell()->web_contents(),
435                             base::ASCIIToUTF16("pushState"));
436  NavigateToURL(shell(), embedded_test_server()->GetURL("/push_state.html"));
437  load_observer.Wait();
438  base::string16 title = title_watcher.WaitAndGetTitle();
439  ASSERT_EQ(title, base::ASCIIToUTF16("pushState"));
440
441  // LoadingStateChanged should be called 4 times: start and stop for the
442  // initial load of push_state.html, and start and stop for the "navigation"
443  // triggered by history.pushState(). However, the start notification for the
444  // history.pushState() navigation should set to_different_document to false.
445  EXPECT_EQ("pushState", shell()->web_contents()->GetURL().ref());
446  EXPECT_EQ(4, delegate->loadingStateChangedCount());
447  EXPECT_EQ(3, delegate->loadingStateToDifferentDocumentCount());
448}
449
450IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
451                       RenderViewCreatedForChildWindow) {
452  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
453
454  NavigateToURL(shell(),
455                embedded_test_server()->GetURL("/title1.html"));
456
457  WebContentsAddedObserver new_web_contents_observer;
458  ASSERT_TRUE(ExecuteScript(shell()->web_contents(),
459                            "var a = document.createElement('a');"
460                            "a.href='./title2.html';"
461                            "a.target = '_blank';"
462                            "document.body.appendChild(a);"
463                            "a.click();"));
464  WebContents* new_web_contents = new_web_contents_observer.GetWebContents();
465  WaitForLoadStop(new_web_contents);
466  EXPECT_TRUE(new_web_contents_observer.RenderViewCreatedCalled());
467}
468
469struct LoadProgressDelegateAndObserver : public WebContentsDelegate,
470                                         public WebContentsObserver {
471  LoadProgressDelegateAndObserver(Shell* shell)
472      : WebContentsObserver(shell->web_contents()),
473        did_start_loading(false),
474        did_stop_loading(false) {
475    web_contents()->SetDelegate(this);
476  }
477
478  // WebContentsDelegate:
479  virtual void LoadProgressChanged(WebContents* source,
480                                   double progress) OVERRIDE {
481    EXPECT_TRUE(did_start_loading);
482    EXPECT_FALSE(did_stop_loading);
483    progresses.push_back(progress);
484  }
485
486  // WebContentsObserver:
487  virtual void DidStartLoading(RenderViewHost* render_view_host) OVERRIDE {
488    EXPECT_FALSE(did_start_loading);
489    EXPECT_EQ(0U, progresses.size());
490    EXPECT_FALSE(did_stop_loading);
491    did_start_loading = true;
492  }
493
494  virtual void DidStopLoading(RenderViewHost* render_view_host) OVERRIDE {
495    EXPECT_TRUE(did_start_loading);
496    EXPECT_GE(progresses.size(), 1U);
497    EXPECT_FALSE(did_stop_loading);
498    did_stop_loading = true;
499  }
500
501  bool did_start_loading;
502  std::vector<double> progresses;
503  bool did_stop_loading;
504};
505
506IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, LoadProgress) {
507  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
508  scoped_ptr<LoadProgressDelegateAndObserver> delegate(
509      new LoadProgressDelegateAndObserver(shell()));
510
511  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
512
513  const std::vector<double>& progresses = delegate->progresses;
514  // All updates should be in order ...
515  if (std::adjacent_find(progresses.begin(),
516                         progresses.end(),
517                         std::greater<double>()) != progresses.end()) {
518    ADD_FAILURE() << "Progress values should be in order: "
519                  << ::testing::PrintToString(progresses);
520  }
521
522  // ... and the last one should be 1.0, meaning complete.
523  ASSERT_GE(progresses.size(), 1U)
524      << "There should be at least one progress update";
525  EXPECT_EQ(1.0, *progresses.rbegin());
526}
527
528struct FirstVisuallyNonEmptyPaintObserver : public WebContentsObserver {
529  FirstVisuallyNonEmptyPaintObserver(Shell* shell)
530      : WebContentsObserver(shell->web_contents()),
531        did_fist_visually_non_empty_paint_(false) {}
532
533  virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE {
534    did_fist_visually_non_empty_paint_ = true;
535    on_did_first_visually_non_empty_paint_.Run();
536  }
537
538  void WaitForDidFirstVisuallyNonEmptyPaint() {
539    if (did_fist_visually_non_empty_paint_)
540      return;
541    base::RunLoop run_loop;
542    on_did_first_visually_non_empty_paint_ = run_loop.QuitClosure();
543    run_loop.Run();
544  }
545
546  base::Closure on_did_first_visually_non_empty_paint_;
547  bool did_fist_visually_non_empty_paint_;
548};
549
550// See: http://crbug.com/395664
551#if defined(OS_ANDROID)
552#define MAYBE_FirstVisuallyNonEmptyPaint DISABLED_FirstVisuallyNonEmptyPaint
553#else
554// http://crbug.com/398471
555#define MAYBE_FirstVisuallyNonEmptyPaint DISABLED_FirstVisuallyNonEmptyPaint
556#endif
557IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
558                       MAYBE_FirstVisuallyNonEmptyPaint) {
559  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
560  scoped_ptr<FirstVisuallyNonEmptyPaintObserver> observer(
561      new FirstVisuallyNonEmptyPaintObserver(shell()));
562
563  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
564
565  observer->WaitForDidFirstVisuallyNonEmptyPaint();
566  ASSERT_TRUE(observer->did_fist_visually_non_empty_paint_);
567}
568
569}  // namespace content
570
571