1 // Copyright 2014 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 "content/test/content_browser_test_utils_internal.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <map>
11 #include <memory>
12 #include <set>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/bind.h"
17 #include "base/containers/stack.h"
18 #include "base/json/json_reader.h"
19 #include "base/stl_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/task/thread_pool.h"
22 #include "base/test/test_timeouts.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "content/browser/renderer_host/delegated_frame_host.h"
25 #include "content/browser/renderer_host/frame_tree_node.h"
26 #include "content/browser/renderer_host/navigator.h"
27 #include "content/browser/renderer_host/render_frame_host_delegate.h"
28 #include "content/browser/renderer_host/render_frame_proxy_host.h"
29 #include "content/browser/web_contents/web_contents_impl.h"
30 #include "content/public/browser/browser_task_traits.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/file_select_listener.h"
33 #include "content/public/browser/navigation_handle.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/common/use_zoom_for_dsf_policy.h"
37 #include "content/public/test/browser_test_utils.h"
38 #include "content/public/test/content_browser_test_utils.h"
39 #include "content/public/test/test_frame_navigation_observer.h"
40 #include "content/public/test/test_navigation_observer.h"
41 #include "content/shell/browser/shell.h"
42 #include "content/shell/browser/shell_javascript_dialog_manager.h"
43 #include "third_party/blink/public/common/frame/frame_visual_properties.h"
44 
45 namespace content {
46 
NavigateFrameToURL(FrameTreeNode * node,const GURL & url)47 bool NavigateFrameToURL(FrameTreeNode* node, const GURL& url) {
48   TestFrameNavigationObserver observer(node);
49   NavigationController::LoadURLParams params(url);
50   params.transition_type = ui::PAGE_TRANSITION_LINK;
51   params.frame_tree_node_id = node->frame_tree_node_id();
52   node->navigator().GetController()->LoadURLWithParams(params);
53   observer.Wait();
54 
55   if (!observer.last_navigation_succeeded()) {
56     DLOG(WARNING) << "Navigation did not succeed: " << url;
57     return false;
58   }
59   if (url != node->current_url()) {
60     DLOG(WARNING) << "Expected URL " << url << " but observed "
61                   << node->current_url();
62     return false;
63   }
64   return true;
65 }
66 
SetShouldProceedOnBeforeUnload(Shell * shell,bool proceed,bool success)67 void SetShouldProceedOnBeforeUnload(Shell* shell, bool proceed, bool success) {
68   ShellJavaScriptDialogManager* manager =
69       static_cast<ShellJavaScriptDialogManager*>(
70           shell->GetJavaScriptDialogManager(shell->web_contents()));
71   manager->set_should_proceed_on_beforeunload(proceed, success);
72 }
73 
ConvertToRenderFrameHost(FrameTreeNode * frame_tree_node)74 RenderFrameHost* ConvertToRenderFrameHost(FrameTreeNode* frame_tree_node) {
75   return frame_tree_node->current_frame_host();
76 }
77 
NavigateToURLInSameBrowsingInstance(Shell * window,const GURL & url)78 bool NavigateToURLInSameBrowsingInstance(Shell* window, const GURL& url) {
79   TestNavigationObserver observer(window->web_contents());
80   // Using a PAGE_TRANSITION_LINK transition with a browser-initiated
81   // navigation forces it to stay in the current BrowsingInstance, as normally
82   // that transition is used by renderer-initiated navigations.
83   window->LoadURLForFrame(url, std::string(),
84                           ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK));
85   observer.Wait();
86 
87   if (!IsLastCommittedEntryOfPageType(window->web_contents(),
88                                       PAGE_TYPE_NORMAL)) {
89     NavigationEntry* last_entry =
90         window->web_contents()->GetController().GetLastCommittedEntry();
91     DLOG(WARNING) << "last_entry->GetPageType() = "
92                   << (last_entry ? last_entry->GetPageType() : -1);
93     return false;
94   }
95 
96   if (window->web_contents()->GetLastCommittedURL() != url) {
97     DLOG(WARNING) << "window->web_contents()->GetLastCommittedURL() = "
98                   << window->web_contents()->GetLastCommittedURL()
99                   << "; url = " << url;
100     return false;
101   }
102 
103   return true;
104 }
105 
FrameTreeVisualizer()106 FrameTreeVisualizer::FrameTreeVisualizer() {
107 }
108 
~FrameTreeVisualizer()109 FrameTreeVisualizer::~FrameTreeVisualizer() {
110 }
111 
DepictFrameTree(FrameTreeNode * root)112 std::string FrameTreeVisualizer::DepictFrameTree(FrameTreeNode* root) {
113   // Tracks the sites actually used in this depiction.
114   std::map<std::string, SiteInstance*> legend;
115 
116   // Traversal 1: Assign names to current frames. This ensures that the first
117   // call to the pretty-printer will result in a naming of the site instances
118   // that feels natural and stable.
119   base::stack<FrameTreeNode*> to_explore;
120   for (to_explore.push(root); !to_explore.empty();) {
121     FrameTreeNode* node = to_explore.top();
122     to_explore.pop();
123     for (size_t i = node->child_count(); i-- != 0;) {
124       to_explore.push(node->child_at(i));
125     }
126 
127     RenderFrameHost* current = node->render_manager()->current_frame_host();
128     legend[GetName(current->GetSiteInstance())] = current->GetSiteInstance();
129   }
130 
131   // Traversal 2: Assign names to the pending/speculative frames. For stability
132   // of assigned names it's important to do this before trying to name the
133   // proxies, which have a less well defined order.
134   for (to_explore.push(root); !to_explore.empty();) {
135     FrameTreeNode* node = to_explore.top();
136     to_explore.pop();
137     for (size_t i = node->child_count(); i-- != 0;) {
138       to_explore.push(node->child_at(i));
139     }
140 
141     RenderFrameHost* spec = node->render_manager()->speculative_frame_host();
142     if (spec)
143       legend[GetName(spec->GetSiteInstance())] = spec->GetSiteInstance();
144   }
145 
146   // Traversal 3: Assign names to the proxies and add them to |legend| too.
147   // Typically, only openers should have their names assigned this way.
148   for (to_explore.push(root); !to_explore.empty();) {
149     FrameTreeNode* node = to_explore.top();
150     to_explore.pop();
151     for (size_t i = node->child_count(); i-- != 0;) {
152       to_explore.push(node->child_at(i));
153     }
154 
155     // Sort the proxies by SiteInstance ID to avoid unordered_map ordering.
156     std::vector<SiteInstance*> site_instances;
157     for (const auto& proxy_pair :
158          node->render_manager()->GetAllProxyHostsForTesting()) {
159       site_instances.push_back(proxy_pair.second->GetSiteInstance());
160     }
161     std::sort(site_instances.begin(), site_instances.end(),
162               [](SiteInstance* lhs, SiteInstance* rhs) {
163                 return lhs->GetId() < rhs->GetId();
164               });
165 
166     for (SiteInstance* site_instance : site_instances)
167       legend[GetName(site_instance)] = site_instance;
168   }
169 
170   // Traversal 4: Now that all names are assigned, make a big loop to pretty-
171   // print the tree. Each iteration produces exactly one line of format.
172   std::string result;
173   for (to_explore.push(root); !to_explore.empty();) {
174     FrameTreeNode* node = to_explore.top();
175     to_explore.pop();
176     for (size_t i = node->child_count(); i-- != 0;) {
177       to_explore.push(node->child_at(i));
178     }
179 
180     // Draw the feeler line tree graphics by walking up to the root. A feeler
181     // line is needed for each ancestor that is the last child of its parent.
182     // This creates the ASCII art that looks like:
183     //    Foo
184     //      |--Foo
185     //      |--Foo
186     //      |    |--Foo
187     //      |    +--Foo
188     //      |         +--Foo
189     //      +--Foo
190     //           +--Foo
191     //
192     // TODO(nick): Make this more elegant.
193     std::string line;
194     if (node != root) {
195       if (node->parent()->child_at(node->parent()->child_count() - 1) != node)
196         line = "  |--";
197       else
198         line = "  +--";
199       for (FrameTreeNode* up = node->parent()->frame_tree_node(); up != root;
200            up = FrameTreeNode::From(up->parent())) {
201         if (up->parent()->child_at(up->parent()->child_count() - 1) != up)
202           line = "  |  " + line;
203         else
204           line = "     " + line;
205       }
206     }
207 
208     // Prefix one extra space of padding for two reasons. First, this helps the
209     // diagram aligns nicely with the legend. Second, this makes it easier to
210     // read the diffs that gtest spits out on EXPECT_EQ failure.
211     line = " " + line;
212 
213     // Summarize the FrameTreeNode's state. Always show the site of the current
214     // RenderFrameHost, and show any exceptional state of the node, like a
215     // pending or speculative RenderFrameHost.
216     RenderFrameHost* current = node->render_manager()->current_frame_host();
217     RenderFrameHost* spec = node->render_manager()->speculative_frame_host();
218     base::StringAppendF(&line, "Site %s",
219                         GetName(current->GetSiteInstance()).c_str());
220     if (spec) {
221       base::StringAppendF(&line, " (%s speculative)",
222                           GetName(spec->GetSiteInstance()).c_str());
223     }
224 
225     // Show the SiteInstances of the RenderFrameProxyHosts of this node.
226     const auto& proxy_host_map =
227         node->render_manager()->GetAllProxyHostsForTesting();
228     if (!proxy_host_map.empty()) {
229       // Show a dashed line of variable length before the proxy list. Always at
230       // least two dashes.
231       line.append(" --");
232 
233       // To make proxy lists align vertically for the first three tree levels,
234       // pad with dashes up to a first tab stop at column 19 (which works out to
235       // text editor column 28 in the typical diagram fed to EXPECT_EQ as a
236       // string literal). Lining the lists up vertically makes differences in
237       // the proxy sets easier to spot visually. We choose not to use the
238       // *actual* tree height here, because that would make the diagram's
239       // appearance less stable as the tree's shape evolves.
240       while (line.length() < 20) {
241         line.append("-");
242       }
243       line.append(" proxies for");
244 
245       // Sort these alphabetically, to avoid hash_map ordering dependency.
246       std::vector<std::string> sorted_proxy_hosts;
247       for (const auto& proxy_pair : proxy_host_map) {
248         sorted_proxy_hosts.push_back(
249             GetName(proxy_pair.second->GetSiteInstance()));
250       }
251       std::sort(sorted_proxy_hosts.begin(), sorted_proxy_hosts.end());
252       for (std::string& proxy_name : sorted_proxy_hosts) {
253         base::StringAppendF(&line, " %s", proxy_name.c_str());
254       }
255     }
256     if (node != root)
257       result.append("\n");
258     result.append(line);
259   }
260 
261   // Finally, show a legend with details of the site instances.
262   const char* prefix = "Where ";
263   for (auto& legend_entry : legend) {
264     SiteInstanceImpl* site_instance =
265         static_cast<SiteInstanceImpl*>(legend_entry.second);
266     std::string description = site_instance->GetSiteURL().spec();
267     base::StringAppendF(&result, "\n%s%s = %s", prefix,
268                         legend_entry.first.c_str(), description.c_str());
269     // Highlight some exceptionable conditions.
270     if (site_instance->active_frame_count() == 0)
271       result.append(" (active_frame_count == 0)");
272     if (!site_instance->GetProcess()->IsInitializedAndNotDead())
273       result.append(" (no process)");
274     prefix = "      ";
275   }
276   return result;
277 }
278 
GetName(SiteInstance * site_instance)279 std::string FrameTreeVisualizer::GetName(SiteInstance* site_instance) {
280   // Indices into the vector correspond to letters of the alphabet.
281   size_t index =
282       std::find(seen_site_instance_ids_.begin(), seen_site_instance_ids_.end(),
283                 site_instance->GetId()) -
284       seen_site_instance_ids_.begin();
285   if (index == seen_site_instance_ids_.size())
286     seen_site_instance_ids_.push_back(site_instance->GetId());
287 
288   // Whosoever writes a test using >=26 site instances shall be a lucky ducky.
289   if (index < 25)
290     return base::StringPrintf("%c", 'A' + static_cast<char>(index));
291   else
292     return base::StringPrintf("Z%d", static_cast<int>(index - 25));
293 }
294 
OpenPopup(const ToRenderFrameHost & opener,const GURL & url,const std::string & name)295 Shell* OpenPopup(const ToRenderFrameHost& opener,
296                  const GURL& url,
297                  const std::string& name) {
298   return OpenPopup(opener, url, name, "", true);
299 }
300 
OpenPopup(const ToRenderFrameHost & opener,const GURL & url,const std::string & name,const std::string & features,bool expect_return_from_window_open)301 Shell* OpenPopup(const ToRenderFrameHost& opener,
302                  const GURL& url,
303                  const std::string& name,
304                  const std::string& features,
305                  bool expect_return_from_window_open) {
306   TestNavigationObserver observer(url);
307   observer.StartWatchingNewWebContents();
308 
309   ShellAddedObserver new_shell_observer;
310   bool did_create_popup = false;
311   std::string popup_script =
312       "window.domAutomationController.send("
313       "    !!window.open('" +
314       url.spec() + "', '" + name + "', '" + features + "'));";
315   bool did_execute_script =
316       ExecuteScriptAndExtractBool(opener, popup_script, &did_create_popup);
317 
318   // Don't check the value of |did_create_popup| since there are valid reasons
319   // for it to be false, e.g. |features| specifies 'noopener', or 'noreferrer'
320   // or others.
321   if (!did_execute_script ||
322       !(did_create_popup || !expect_return_from_window_open)) {
323     return nullptr;
324   }
325 
326   observer.Wait();
327 
328   Shell* new_shell = new_shell_observer.GetShell();
329   EXPECT_EQ(url,
330             new_shell->web_contents()->GetMainFrame()->GetLastCommittedURL());
331   return new_shell_observer.GetShell();
332 }
333 
FileChooserDelegate(const base::FilePath & file,base::OnceClosure callback)334 FileChooserDelegate::FileChooserDelegate(const base::FilePath& file,
335                                          base::OnceClosure callback)
336     : file_(file), callback_(std::move(callback)) {}
337 
338 FileChooserDelegate::~FileChooserDelegate() = default;
339 
RunFileChooser(RenderFrameHost * render_frame_host,scoped_refptr<content::FileSelectListener> listener,const blink::mojom::FileChooserParams & params)340 void FileChooserDelegate::RunFileChooser(
341     RenderFrameHost* render_frame_host,
342     scoped_refptr<content::FileSelectListener> listener,
343     const blink::mojom::FileChooserParams& params) {
344   // Send the selected file to the renderer process.
345   auto file_info = blink::mojom::FileChooserFileInfo::NewNativeFile(
346       blink::mojom::NativeFileInfo::New(file_, base::string16()));
347   std::vector<blink::mojom::FileChooserFileInfoPtr> files;
348   files.push_back(std::move(file_info));
349   listener->FileSelected(std::move(files), base::FilePath(),
350                          blink::mojom::FileChooserParams::Mode::kOpen);
351 
352   params_ = params.Clone();
353   std::move(callback_).Run();
354 }
355 
FrameTestNavigationManager(int filtering_frame_tree_node_id,WebContents * web_contents,const GURL & url)356 FrameTestNavigationManager::FrameTestNavigationManager(
357     int filtering_frame_tree_node_id,
358     WebContents* web_contents,
359     const GURL& url)
360     : TestNavigationManager(web_contents, url),
361       filtering_frame_tree_node_id_(filtering_frame_tree_node_id) {}
362 
ShouldMonitorNavigation(NavigationHandle * handle)363 bool FrameTestNavigationManager::ShouldMonitorNavigation(
364     NavigationHandle* handle) {
365   return TestNavigationManager::ShouldMonitorNavigation(handle) &&
366          handle->GetFrameTreeNodeId() == filtering_frame_tree_node_id_;
367 }
368 
UrlCommitObserver(FrameTreeNode * frame_tree_node,const GURL & url)369 UrlCommitObserver::UrlCommitObserver(FrameTreeNode* frame_tree_node,
370                                      const GURL& url)
371     : content::WebContentsObserver(frame_tree_node->current_frame_host()
372                                        ->delegate()
373                                        ->GetAsWebContents()),
374       frame_tree_node_id_(frame_tree_node->frame_tree_node_id()),
375       url_(url) {
376 }
377 
~UrlCommitObserver()378 UrlCommitObserver::~UrlCommitObserver() {}
379 
Wait()380 void UrlCommitObserver::Wait() {
381   run_loop_.Run();
382 }
383 
DidFinishNavigation(NavigationHandle * navigation_handle)384 void UrlCommitObserver::DidFinishNavigation(
385     NavigationHandle* navigation_handle) {
386   if (navigation_handle->HasCommitted() &&
387       !navigation_handle->IsErrorPage() &&
388       navigation_handle->GetURL() == url_ &&
389       navigation_handle->GetFrameTreeNodeId() == frame_tree_node_id_) {
390     run_loop_.Quit();
391   }
392 }
393 
RenderProcessHostBadIpcMessageWaiter(RenderProcessHost * render_process_host)394 RenderProcessHostBadIpcMessageWaiter::RenderProcessHostBadIpcMessageWaiter(
395     RenderProcessHost* render_process_host)
396     : internal_waiter_(render_process_host,
397                        "Stability.BadMessageTerminated.Content") {}
398 
399 base::Optional<bad_message::BadMessageReason>
Wait()400 RenderProcessHostBadIpcMessageWaiter::Wait() {
401   base::Optional<int> internal_result = internal_waiter_.Wait();
402   if (!internal_result.has_value())
403     return base::nullopt;
404   return static_cast<bad_message::BadMessageReason>(internal_result.value());
405 }
406 
ShowPopupWidgetWaiter(WebContents * web_contents,RenderFrameHostImpl * frame_host)407 ShowPopupWidgetWaiter::ShowPopupWidgetWaiter(WebContents* web_contents,
408                                              RenderFrameHostImpl* frame_host)
409     : WebContentsObserver(web_contents), frame_host_(frame_host) {
410   frame_host_->SetCreateNewPopupCallbackForTesting(base::BindRepeating(
411       &ShowPopupWidgetWaiter::DidCreatePopupWidget, base::Unretained(this)));
412 }
413 
~ShowPopupWidgetWaiter()414 ShowPopupWidgetWaiter::~ShowPopupWidgetWaiter() {
415   if (auto* rwhi = RenderWidgetHostImpl::FromID(process_id_, routing_id_)) {
416     rwhi->popup_widget_host_receiver_for_testing().SwapImplForTesting(rwhi);
417   }
418   if (frame_host_)
419     frame_host_->SetCreateNewPopupCallbackForTesting(base::NullCallback());
420 }
421 
Wait()422 void ShowPopupWidgetWaiter::Wait() {
423   run_loop_.Run();
424 }
425 
Stop()426 void ShowPopupWidgetWaiter::Stop() {
427   Observe(nullptr);
428   frame_host_->SetCreateNewPopupCallbackForTesting(base::NullCallback());
429   frame_host_ = nullptr;
430 }
431 
GetForwardingInterface()432 blink::mojom::PopupWidgetHost* ShowPopupWidgetWaiter::GetForwardingInterface() {
433   DCHECK_NE(MSG_ROUTING_NONE, routing_id_);
434   return RenderWidgetHostImpl::FromID(process_id_, routing_id_);
435 }
436 
ShowPopup(const gfx::Rect & initial_rect,ShowPopupCallback callback)437 void ShowPopupWidgetWaiter::ShowPopup(const gfx::Rect& initial_rect,
438                                       ShowPopupCallback callback) {
439   GetForwardingInterface()->ShowPopup(initial_rect, std::move(callback));
440   initial_rect_ = initial_rect;
441   run_loop_.Quit();
442 }
443 
DidCreatePopupWidget(RenderWidgetHostImpl * render_widget_host)444 void ShowPopupWidgetWaiter::DidCreatePopupWidget(
445     RenderWidgetHostImpl* render_widget_host) {
446   process_id_ = render_widget_host->GetProcess()->GetID();
447   routing_id_ = render_widget_host->GetRoutingID();
448   render_widget_host->popup_widget_host_receiver_for_testing()
449       .SwapImplForTesting(this);
450 }
451 
452 #if defined(OS_MAC) || defined(OS_ANDROID)
ShowPopupMenu(RenderFrameHost * render_frame_host,mojo::PendingRemote<blink::mojom::PopupMenuClient> * popup_client,const gfx::Rect & bounds,int32_t item_height,double font_size,int32_t selected_item,std::vector<blink::mojom::MenuItemPtr> * menu_items,bool right_aligned,bool allow_multiple_selection)453 bool ShowPopupWidgetWaiter::ShowPopupMenu(
454     RenderFrameHost* render_frame_host,
455     mojo::PendingRemote<blink::mojom::PopupMenuClient>* popup_client,
456     const gfx::Rect& bounds,
457     int32_t item_height,
458     double font_size,
459     int32_t selected_item,
460     std::vector<blink::mojom::MenuItemPtr>* menu_items,
461     bool right_aligned,
462     bool allow_multiple_selection) {
463   initial_rect_ = bounds;
464   run_loop_.Quit();
465   return true;
466 }
467 #endif
468 
DropMessageFilter(uint32_t message_class,uint32_t drop_message_id)469 DropMessageFilter::DropMessageFilter(uint32_t message_class,
470                                      uint32_t drop_message_id)
471     : BrowserMessageFilter(message_class), drop_message_id_(drop_message_id) {}
472 
473 DropMessageFilter::~DropMessageFilter() = default;
474 
OnMessageReceived(const IPC::Message & message)475 bool DropMessageFilter::OnMessageReceived(const IPC::Message& message) {
476   return message.type() == drop_message_id_;
477 }
478 
ObserveMessageFilter(uint32_t message_class,uint32_t watch_message_id)479 ObserveMessageFilter::ObserveMessageFilter(uint32_t message_class,
480                                            uint32_t watch_message_id)
481     : BrowserMessageFilter(message_class),
482       watch_message_id_(watch_message_id) {}
483 
484 ObserveMessageFilter::~ObserveMessageFilter() = default;
485 
Wait()486 void ObserveMessageFilter::Wait() {
487   base::RunLoop loop;
488   quit_closure_ = loop.QuitClosure();
489   loop.Run();
490 }
491 
OnMessageReceived(const IPC::Message & message)492 bool ObserveMessageFilter::OnMessageReceived(const IPC::Message& message) {
493   if (message.type() == watch_message_id_) {
494     // Exit the Wait() method if it's being used, but in a fresh stack once the
495     // message is actually handled.
496     if (quit_closure_ && !received_) {
497       base::ThreadPool::PostTask(
498           FROM_HERE, base::BindOnce(&ObserveMessageFilter::QuitWait, this));
499     }
500     received_ = true;
501   }
502   return false;
503 }
504 
QuitWait()505 void ObserveMessageFilter::QuitWait() {
506   std::move(quit_closure_).Run();
507 }
508 
UnresponsiveRendererObserver(WebContents * web_contents)509 UnresponsiveRendererObserver::UnresponsiveRendererObserver(
510     WebContents* web_contents)
511     : WebContentsObserver(web_contents) {}
512 
513 UnresponsiveRendererObserver::~UnresponsiveRendererObserver() = default;
514 
Wait(base::TimeDelta timeout)515 RenderProcessHost* UnresponsiveRendererObserver::Wait(base::TimeDelta timeout) {
516   if (!captured_render_process_host_) {
517     base::OneShotTimer timer;
518     timer.Start(FROM_HERE, timeout, run_loop_.QuitClosure());
519     run_loop_.Run();
520     timer.Stop();
521   }
522   return captured_render_process_host_;
523 }
524 
OnRendererUnresponsive(RenderProcessHost * render_process_host)525 void UnresponsiveRendererObserver::OnRendererUnresponsive(
526     RenderProcessHost* render_process_host) {
527   captured_render_process_host_ = render_process_host;
528   run_loop_.Quit();
529 }
530 
BeforeUnloadBlockingDelegate(WebContentsImpl * web_contents)531 BeforeUnloadBlockingDelegate::BeforeUnloadBlockingDelegate(
532     WebContentsImpl* web_contents)
533     : web_contents_(web_contents) {
534   web_contents_->SetDelegate(this);
535 }
536 
~BeforeUnloadBlockingDelegate()537 BeforeUnloadBlockingDelegate::~BeforeUnloadBlockingDelegate() {
538   if (!callback_.is_null())
539     std::move(callback_).Run(true, base::string16());
540 
541   web_contents_->SetDelegate(nullptr);
542   web_contents_->SetJavaScriptDialogManagerForTesting(nullptr);
543 }
544 
Wait()545 void BeforeUnloadBlockingDelegate::Wait() {
546   run_loop_->Run();
547   run_loop_ = std::make_unique<base::RunLoop>();
548 }
549 
550 JavaScriptDialogManager*
GetJavaScriptDialogManager(WebContents * source)551 BeforeUnloadBlockingDelegate::GetJavaScriptDialogManager(WebContents* source) {
552   return this;
553 }
554 
RunJavaScriptDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,JavaScriptDialogType dialog_type,const base::string16 & message_text,const base::string16 & default_prompt_text,DialogClosedCallback callback,bool * did_suppress_message)555 void BeforeUnloadBlockingDelegate::RunJavaScriptDialog(
556     WebContents* web_contents,
557     RenderFrameHost* render_frame_host,
558     JavaScriptDialogType dialog_type,
559     const base::string16& message_text,
560     const base::string16& default_prompt_text,
561     DialogClosedCallback callback,
562     bool* did_suppress_message) {
563   NOTREACHED();
564 }
565 
RunBeforeUnloadDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,bool is_reload,DialogClosedCallback callback)566 void BeforeUnloadBlockingDelegate::RunBeforeUnloadDialog(
567     WebContents* web_contents,
568     RenderFrameHost* render_frame_host,
569     bool is_reload,
570     DialogClosedCallback callback) {
571   callback_ = std::move(callback);
572   run_loop_->Quit();
573 }
574 
HandleJavaScriptDialog(WebContents * web_contents,bool accept,const base::string16 * prompt_override)575 bool BeforeUnloadBlockingDelegate::HandleJavaScriptDialog(
576     WebContents* web_contents,
577     bool accept,
578     const base::string16* prompt_override) {
579   NOTREACHED();
580   return true;
581 }
582 
583 namespace {
584 static constexpr int kEnableLogMessageId = 0;
585 static constexpr char kEnableLogMessage[] = R"({"id":0,"method":"Log.enable"})";
586 static constexpr int kDisableLogMessageId = 1;
587 static constexpr char kDisableLogMessage[] =
588     R"({"id":1,"method":"Log.disable"})";
589 }  // namespace
590 
DevToolsInspectorLogWatcher(WebContents * web_contents)591 DevToolsInspectorLogWatcher::DevToolsInspectorLogWatcher(
592     WebContents* web_contents) {
593   host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
594   host_->AttachClient(this);
595 
596   host_->DispatchProtocolMessage(
597       this, base::as_bytes(
598                 base::make_span(kEnableLogMessage, strlen(kEnableLogMessage))));
599 
600   run_loop_enable_log_.Run();
601 }
602 
~DevToolsInspectorLogWatcher()603 DevToolsInspectorLogWatcher::~DevToolsInspectorLogWatcher() {
604   host_->DetachClient(this);
605 }
606 
DispatchProtocolMessage(DevToolsAgentHost * host,base::span<const uint8_t> message)607 void DevToolsInspectorLogWatcher::DispatchProtocolMessage(
608     DevToolsAgentHost* host,
609     base::span<const uint8_t> message) {
610   base::StringPiece message_str(reinterpret_cast<const char*>(message.data()),
611                                 message.size());
612   auto parsed_message = base::JSONReader::Read(message_str);
613   base::Optional<int> command_id = parsed_message->FindIntPath("id");
614   if (command_id.has_value()) {
615     switch (command_id.value()) {
616       case kEnableLogMessageId:
617         run_loop_enable_log_.Quit();
618         break;
619       case kDisableLogMessageId:
620         run_loop_disable_log_.Quit();
621         break;
622       default:
623         NOTREACHED();
624     }
625     return;
626   }
627 
628   std::string* notification = parsed_message->FindStringPath("method");
629   if (notification && *notification == "Log.entryAdded") {
630     std::string* text = parsed_message->FindStringPath("params.entry.text");
631     DCHECK(text);
632     last_message_ = *text;
633   }
634 }
635 
AgentHostClosed(DevToolsAgentHost * host)636 void DevToolsInspectorLogWatcher::AgentHostClosed(DevToolsAgentHost* host) {}
637 
FlushAndStopWatching()638 void DevToolsInspectorLogWatcher::FlushAndStopWatching() {
639   host_->DispatchProtocolMessage(
640       this, base::as_bytes(base::make_span(kDisableLogMessage,
641                                            strlen(kDisableLogMessage))));
642   run_loop_disable_log_.Run();
643 }
644 
645 }  // namespace content
646