1 // Copyright (c) 2012 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 "chrome/browser/devtools/devtools_window.h"
6 
7 #include <algorithm>
8 #include <set>
9 #include <utility>
10 
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/json/json_reader.h"
15 #include "base/macros.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/metrics/user_metrics.h"
19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/certificate_viewer.h"
23 #include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
24 #include "chrome/browser/devtools/devtools_eye_dropper.h"
25 #include "chrome/browser/file_select_helper.h"
26 #include "chrome/browser/infobars/infobar_service.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/task_manager/web_contents_tags.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/browser/ui/browser_tabstrip.h"
32 #include "chrome/browser/ui/browser_window.h"
33 #include "chrome/browser/ui/color_chooser.h"
34 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
35 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
36 #include "chrome/browser/ui/tabs/tab_strip_model.h"
37 #include "chrome/browser/ui/webui/devtools_ui.h"
38 #include "chrome/common/chrome_switches.h"
39 #include "chrome/common/pref_names.h"
40 #include "chrome/common/url_constants.h"
41 #include "components/javascript_dialogs/app_modal_dialog_manager.h"
42 #include "components/keep_alive_registry/keep_alive_types.h"
43 #include "components/keep_alive_registry/scoped_keep_alive.h"
44 #include "components/language/core/browser/pref_names.h"
45 #include "components/pref_registry/pref_registry_syncable.h"
46 #include "components/prefs/scoped_user_pref_update.h"
47 #include "components/sessions/content/session_tab_helper.h"
48 #include "components/sync_preferences/pref_service_syncable.h"
49 #include "components/web_modal/web_contents_modal_dialog_manager.h"
50 #include "components/zoom/page_zoom.h"
51 #include "components/zoom/zoom_controller.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "content/public/browser/child_process_security_policy.h"
54 #include "content/public/browser/devtools_agent_host.h"
55 #include "content/public/browser/file_select_listener.h"
56 #include "content/public/browser/keyboard_event_processing_result.h"
57 #include "content/public/browser/native_web_keyboard_event.h"
58 #include "content/public/browser/navigation_controller.h"
59 #include "content/public/browser/navigation_entry.h"
60 #include "content/public/browser/navigation_handle.h"
61 #include "content/public/browser/navigation_throttle.h"
62 #include "content/public/browser/render_frame_host.h"
63 #include "content/public/browser/render_process_host.h"
64 #include "content/public/browser/render_view_host.h"
65 #include "content/public/browser/render_widget_host_view.h"
66 #include "content/public/browser/web_contents.h"
67 #include "content/public/common/content_client.h"
68 #include "content/public/common/url_constants.h"
69 #include "net/base/escape.h"
70 #include "third_party/blink/public/common/input/web_gesture_event.h"
71 #include "third_party/blink/public/common/input/web_input_event.h"
72 #include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
73 #include "third_party/blink/public/public_buildflags.h"
74 #include "ui/base/page_transition_types.h"
75 #include "ui/events/keycodes/dom/keycode_converter.h"
76 #include "ui/events/keycodes/keyboard_code_conversion.h"
77 #include "ui/events/keycodes/keyboard_codes.h"
78 
79 using base::DictionaryValue;
80 using blink::WebInputEvent;
81 using content::BrowserThread;
82 using content::DevToolsAgentHost;
83 using content::WebContents;
84 
85 namespace {
86 
87 typedef std::vector<DevToolsWindow*> DevToolsWindows;
88 base::LazyInstance<DevToolsWindows>::Leaky g_devtools_window_instances =
89     LAZY_INSTANCE_INITIALIZER;
90 
91 base::LazyInstance<std::vector<base::Callback<void(DevToolsWindow*)>>>::Leaky
92     g_creation_callbacks = LAZY_INSTANCE_INITIALIZER;
93 
94 static const char kKeyUpEventName[] = "keyup";
95 static const char kKeyDownEventName[] = "keydown";
96 static const char kDefaultFrontendURL[] =
97     "devtools://devtools/bundled/devtools_app.html";
98 static const char kNodeFrontendURL[] =
99     "devtools://devtools/bundled/node_app.html";
100 static const char kWorkerFrontendURL[] =
101     "devtools://devtools/bundled/worker_app.html";
102 static const char kJSFrontendURL[] = "devtools://devtools/bundled/js_app.html";
103 static const char kFallbackFrontendURL[] =
104     "devtools://devtools/bundled/inspector.html";
105 
FindInspectedBrowserAndTabIndex(WebContents * inspected_web_contents,Browser ** browser,int * tab)106 bool FindInspectedBrowserAndTabIndex(
107     WebContents* inspected_web_contents, Browser** browser, int* tab) {
108   if (!inspected_web_contents)
109     return false;
110 
111   for (auto* b : *BrowserList::GetInstance()) {
112     int tab_index =
113         b->tab_strip_model()->GetIndexOfWebContents(inspected_web_contents);
114     if (tab_index != TabStripModel::kNoTab) {
115       *browser = b;
116       *tab = tab_index;
117       return true;
118     }
119   }
120   return false;
121 }
122 
SetPreferencesFromJson(Profile * profile,const std::string & json)123 void SetPreferencesFromJson(Profile* profile, const std::string& json) {
124   base::Optional<base::Value> parsed = base::JSONReader::Read(json);
125   if (!parsed || !parsed->is_dict())
126     return;
127   DictionaryPrefUpdate update(profile->GetPrefs(), prefs::kDevToolsPreferences);
128   for (const auto& dict_value : parsed->DictItems()) {
129     if (!dict_value.second.is_string())
130       continue;
131     update.Get()->SetKey(dict_value.first, std::move(dict_value.second));
132   }
133 }
134 
135 // DevToolsToolboxDelegate ----------------------------------------------------
136 
137 class DevToolsToolboxDelegate
138     : public content::WebContentsObserver,
139       public content::WebContentsDelegate {
140  public:
141   DevToolsToolboxDelegate(
142       WebContents* toolbox_contents,
143       DevToolsWindow::ObserverWithAccessor* web_contents_observer);
144   ~DevToolsToolboxDelegate() override;
145 
146   content::WebContents* OpenURLFromTab(
147       content::WebContents* source,
148       const content::OpenURLParams& params) override;
149   content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
150       content::WebContents* source,
151       const content::NativeWebKeyboardEvent& event) override;
152   bool HandleKeyboardEvent(
153       content::WebContents* source,
154       const content::NativeWebKeyboardEvent& event) override;
155   void WebContentsDestroyed() override;
156 
157  private:
158   BrowserWindow* GetInspectedBrowserWindow();
159   DevToolsWindow::ObserverWithAccessor* inspected_contents_observer_;
160   DISALLOW_COPY_AND_ASSIGN(DevToolsToolboxDelegate);
161 };
162 
DevToolsToolboxDelegate(WebContents * toolbox_contents,DevToolsWindow::ObserverWithAccessor * web_contents_observer)163 DevToolsToolboxDelegate::DevToolsToolboxDelegate(
164     WebContents* toolbox_contents,
165     DevToolsWindow::ObserverWithAccessor* web_contents_observer)
166     : WebContentsObserver(toolbox_contents),
167       inspected_contents_observer_(web_contents_observer) {
168 }
169 
~DevToolsToolboxDelegate()170 DevToolsToolboxDelegate::~DevToolsToolboxDelegate() {
171 }
172 
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)173 content::WebContents* DevToolsToolboxDelegate::OpenURLFromTab(
174     content::WebContents* source,
175     const content::OpenURLParams& params) {
176   DCHECK(source == web_contents());
177   if (!params.url.SchemeIs(content::kChromeDevToolsScheme))
178     return nullptr;
179   source->GetController().LoadURLWithParams(
180       content::NavigationController::LoadURLParams(params));
181   return source;
182 }
183 
184 content::KeyboardEventProcessingResult
PreHandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)185 DevToolsToolboxDelegate::PreHandleKeyboardEvent(
186     content::WebContents* source,
187     const content::NativeWebKeyboardEvent& event) {
188   BrowserWindow* window = GetInspectedBrowserWindow();
189   if (window)
190     return window->PreHandleKeyboardEvent(event);
191   return content::KeyboardEventProcessingResult::NOT_HANDLED;
192 }
193 
HandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)194 bool DevToolsToolboxDelegate::HandleKeyboardEvent(
195     content::WebContents* source,
196     const content::NativeWebKeyboardEvent& event) {
197   if (event.windows_key_code == 0x08) {
198     // Do not navigate back in history on Windows (http://crbug.com/74156).
199     return false;
200   }
201   BrowserWindow* window = GetInspectedBrowserWindow();
202   return window && window->HandleKeyboardEvent(event);
203 }
204 
WebContentsDestroyed()205 void DevToolsToolboxDelegate::WebContentsDestroyed() {
206   delete this;
207 }
208 
GetInspectedBrowserWindow()209 BrowserWindow* DevToolsToolboxDelegate::GetInspectedBrowserWindow() {
210   WebContents* inspected_contents =
211       inspected_contents_observer_->web_contents();
212   if (!inspected_contents)
213     return nullptr;
214   Browser* browser = nullptr;
215   int tab = 0;
216   if (FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
217     return browser->window();
218   return nullptr;
219 }
220 
221 // static
DecorateFrontendURL(const GURL & base_url)222 GURL DecorateFrontendURL(const GURL& base_url) {
223   std::string frontend_url = base_url.spec();
224   std::string url_string(
225       frontend_url +
226       ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
227       "dockSide=undocked");  // TODO(dgozman): remove this support in M38.
228   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
229 
230   if (command_line->HasSwitch(switches::kDevToolsFlags)) {
231     url_string += "&" + command_line->GetSwitchValueASCII(
232         switches::kDevToolsFlags);
233   }
234 
235   if (command_line->HasSwitch(switches::kCustomDevtoolsFrontend)) {
236     url_string += "&debugFrontend=true";
237   }
238 
239   return GURL(url_string);
240 }
241 
242 }  // namespace
243 
244 // DevToolsEventForwarder -----------------------------------------------------
245 
246 class DevToolsEventForwarder {
247  public:
DevToolsEventForwarder(DevToolsWindow * window)248   explicit DevToolsEventForwarder(DevToolsWindow* window)
249      : devtools_window_(window) {}
250 
251   // Registers whitelisted shortcuts with the forwarder.
252   // Only registered keys will be forwarded to the DevTools frontend.
253   void SetWhitelistedShortcuts(const std::string& message);
254 
255   // Forwards a keyboard event to the DevTools frontend if it is whitelisted.
256   // Returns |true| if the event has been forwarded, |false| otherwise.
257   bool ForwardEvent(const content::NativeWebKeyboardEvent& event);
258 
259  private:
260   static bool KeyWhitelistingAllowed(int key_code, int modifiers);
261   static int CombineKeyCodeAndModifiers(int key_code, int modifiers);
262 
263   DevToolsWindow* devtools_window_;
264   std::set<int> whitelisted_keys_;
265 
266   DISALLOW_COPY_AND_ASSIGN(DevToolsEventForwarder);
267 };
268 
SetWhitelistedShortcuts(const std::string & message)269 void DevToolsEventForwarder::SetWhitelistedShortcuts(
270     const std::string& message) {
271   base::Optional<base::Value> parsed_message = base::JSONReader::Read(message);
272   if (!parsed_message || !parsed_message->is_list())
273     return;
274   for (const auto& list_item : parsed_message->GetList()) {
275     if (!list_item.is_dict())
276       continue;
277     int key_code = list_item.FindIntKey("keyCode").value_or(0);
278     if (key_code == 0)
279       continue;
280     int modifiers = list_item.FindIntKey("modifiers").value_or(0);
281     if (!KeyWhitelistingAllowed(key_code, modifiers)) {
282       LOG(WARNING) << "Key whitelisting forbidden: "
283                    << "(" << key_code << "," << modifiers << ")";
284       continue;
285     }
286     whitelisted_keys_.insert(CombineKeyCodeAndModifiers(key_code, modifiers));
287   }
288 }
289 
ForwardEvent(const content::NativeWebKeyboardEvent & event)290 bool DevToolsEventForwarder::ForwardEvent(
291     const content::NativeWebKeyboardEvent& event) {
292   std::string event_type;
293   switch (event.GetType()) {
294     case WebInputEvent::Type::kKeyDown:
295     case WebInputEvent::Type::kRawKeyDown:
296       event_type = kKeyDownEventName;
297       break;
298     case WebInputEvent::Type::kKeyUp:
299       event_type = kKeyUpEventName;
300       break;
301     default:
302       return false;
303   }
304 
305   int key_code = ui::LocatedToNonLocatedKeyboardCode(
306       static_cast<ui::KeyboardCode>(event.windows_key_code));
307   int modifiers = event.GetModifiers() &
308                   (WebInputEvent::kShiftKey | WebInputEvent::kControlKey |
309                    WebInputEvent::kAltKey | WebInputEvent::kMetaKey);
310   int key = CombineKeyCodeAndModifiers(key_code, modifiers);
311   if (whitelisted_keys_.find(key) == whitelisted_keys_.end())
312     return false;
313 
314   base::Value event_data(base::Value::Type::DICTIONARY);
315   event_data.SetStringKey("type", event_type);
316   event_data.SetStringKey("key", ui::KeycodeConverter::DomKeyToKeyString(
317                                      static_cast<ui::DomKey>(event.dom_key)));
318   event_data.SetStringKey("code",
319                           ui::KeycodeConverter::DomCodeToCodeString(
320                               static_cast<ui::DomCode>(event.dom_code)));
321   event_data.SetIntKey("keyCode", key_code);
322   event_data.SetIntKey("modifiers", modifiers);
323   devtools_window_->bindings_->CallClientMethod(
324       "DevToolsAPI", "keyEventUnhandled", std::move(event_data));
325   return true;
326 }
327 
CombineKeyCodeAndModifiers(int key_code,int modifiers)328 int DevToolsEventForwarder::CombineKeyCodeAndModifiers(int key_code,
329                                                        int modifiers) {
330   return key_code | (modifiers << 16);
331 }
332 
KeyWhitelistingAllowed(int key_code,int modifiers)333 bool DevToolsEventForwarder::KeyWhitelistingAllowed(int key_code,
334                                                     int modifiers) {
335   return (ui::VKEY_F1 <= key_code && key_code <= ui::VKEY_F12) ||
336       modifiers != 0;
337 }
338 
OpenNodeFrontend()339 void DevToolsWindow::OpenNodeFrontend() {
340   DevToolsWindow::OpenNodeFrontendWindow(profile_);
341 }
342 
343 // DevToolsWindow::ObserverWithAccessor -------------------------------
344 
ObserverWithAccessor(WebContents * web_contents)345 DevToolsWindow::ObserverWithAccessor::ObserverWithAccessor(
346     WebContents* web_contents)
347     : WebContentsObserver(web_contents) {
348 }
349 
~ObserverWithAccessor()350 DevToolsWindow::ObserverWithAccessor::~ObserverWithAccessor() {
351 }
352 
353 // DevToolsWindow::Throttle ------------------------------------------
354 
355 class DevToolsWindow::Throttle : public content::NavigationThrottle {
356  public:
Throttle(content::NavigationHandle * navigation_handle,DevToolsWindow * devtools_window)357   Throttle(content::NavigationHandle* navigation_handle,
358            DevToolsWindow* devtools_window)
359       : content::NavigationThrottle(navigation_handle),
360         devtools_window_(devtools_window) {
361     devtools_window_->throttle_ = this;
362   }
363 
~Throttle()364   ~Throttle() override {
365     if (devtools_window_)
366       devtools_window_->throttle_ = nullptr;
367   }
368 
369   // content::NavigationThrottle implementation:
WillStartRequest()370   NavigationThrottle::ThrottleCheckResult WillStartRequest() override {
371     return DEFER;
372   }
373 
GetNameForLogging()374   const char* GetNameForLogging() override { return "DevToolsWindowThrottle"; }
375 
ResumeThrottle()376   void ResumeThrottle() {
377     if (devtools_window_) {
378       devtools_window_->throttle_ = nullptr;
379       devtools_window_ = nullptr;
380     }
381     Resume();
382   }
383 
384  private:
385   DevToolsWindow* devtools_window_;
386 
387   DISALLOW_COPY_AND_ASSIGN(Throttle);
388 };
389 
390 // Helper class that holds the owned main WebContents for the docked
391 // devtools window and maintains a keepalive object that keeps the browser
392 // main loop alive long enough for the WebContents to clean up properly.
393 class DevToolsWindow::OwnedMainWebContents {
394  public:
OwnedMainWebContents(std::unique_ptr<content::WebContents> web_contents)395   explicit OwnedMainWebContents(
396       std::unique_ptr<content::WebContents> web_contents)
397       : keep_alive_(KeepAliveOrigin::DEVTOOLS_WINDOW,
398                     KeepAliveRestartOption::DISABLED),
399         web_contents_(std::move(web_contents)) {}
400 
TakeWebContents(std::unique_ptr<OwnedMainWebContents> instance)401   static std::unique_ptr<content::WebContents> TakeWebContents(
402       std::unique_ptr<OwnedMainWebContents> instance) {
403     return std::move(instance->web_contents_);
404   }
405 
406  private:
407   ScopedKeepAlive keep_alive_;
408   std::unique_ptr<content::WebContents> web_contents_;
409 };
410 
411 // DevToolsWindow -------------------------------------------------------------
412 
413 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
414 
415 // static
AddCreationCallbackForTest(const CreationCallback & callback)416 void DevToolsWindow::AddCreationCallbackForTest(
417     const CreationCallback& callback) {
418   g_creation_callbacks.Get().push_back(callback);
419 }
420 
421 // static
RemoveCreationCallbackForTest(const CreationCallback & callback)422 void DevToolsWindow::RemoveCreationCallbackForTest(
423     const CreationCallback& callback) {
424   for (size_t i = 0; i < g_creation_callbacks.Get().size(); ++i) {
425     if (g_creation_callbacks.Get().at(i) == callback) {
426       g_creation_callbacks.Get().erase(g_creation_callbacks.Get().begin() + i);
427       return;
428     }
429   }
430 }
431 
~DevToolsWindow()432 DevToolsWindow::~DevToolsWindow() {
433   if (throttle_)
434     throttle_->ResumeThrottle();
435 
436   if (reattach_complete_callback_) {
437     std::move(reattach_complete_callback_).Run();
438   }
439 
440   life_stage_ = kClosing;
441 
442   UpdateBrowserWindow();
443   UpdateBrowserToolbar();
444 
445   owned_toolbox_web_contents_.reset();
446 
447   DevToolsWindows* instances = g_devtools_window_instances.Pointer();
448   auto it(std::find(instances->begin(), instances->end(), this));
449   DCHECK(it != instances->end());
450   instances->erase(it);
451 
452   if (!close_callback_.is_null())
453     std::move(close_callback_).Run();
454   // Defer deletion of the main web contents, since we could get here
455   // via RenderFrameHostImpl method that expects WebContents to live
456   // for some time. See http://crbug.com/997299 for details.
457   if (owned_main_web_contents_) {
458     base::SequencedTaskRunnerHandle::Get()->DeleteSoon(
459         FROM_HERE, std::move(owned_main_web_contents_));
460   }
461 }
462 
463 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)464 void DevToolsWindow::RegisterProfilePrefs(
465     user_prefs::PrefRegistrySyncable* registry) {
466   registry->RegisterDictionaryPref(prefs::kDevToolsEditedFiles);
467   registry->RegisterDictionaryPref(prefs::kDevToolsFileSystemPaths);
468   registry->RegisterStringPref(prefs::kDevToolsAdbKey, std::string());
469 
470   registry->RegisterBooleanPref(prefs::kDevToolsDiscoverUsbDevicesEnabled,
471                                 true);
472   registry->RegisterBooleanPref(prefs::kDevToolsPortForwardingEnabled, false);
473   registry->RegisterBooleanPref(prefs::kDevToolsPortForwardingDefaultSet,
474                                 false);
475   registry->RegisterDictionaryPref(prefs::kDevToolsPortForwardingConfig);
476   registry->RegisterBooleanPref(prefs::kDevToolsDiscoverTCPTargetsEnabled,
477                                 true);
478   registry->RegisterListPref(prefs::kDevToolsTCPDiscoveryConfig);
479   registry->RegisterDictionaryPref(prefs::kDevToolsPreferences);
480 }
481 
482 // static
GetInTabWebContents(WebContents * inspected_web_contents,DevToolsContentsResizingStrategy * out_strategy)483 content::WebContents* DevToolsWindow::GetInTabWebContents(
484     WebContents* inspected_web_contents,
485     DevToolsContentsResizingStrategy* out_strategy) {
486   DevToolsWindow* window = GetInstanceForInspectedWebContents(
487       inspected_web_contents);
488   if (!window || window->life_stage_ == kClosing)
489     return nullptr;
490 
491   // Not yet loaded window is treated as docked, but we should not present it
492   // until we decided on docking.
493   bool is_docked_set = window->life_stage_ == kLoadCompleted ||
494       window->life_stage_ == kIsDockedSet;
495   if (!is_docked_set)
496     return nullptr;
497 
498   // Undocked window should have toolbox web contents.
499   if (!window->is_docked_ && !window->toolbox_web_contents_)
500     return nullptr;
501 
502   if (out_strategy)
503     out_strategy->CopyFrom(window->contents_resizing_strategy_);
504 
505   return window->is_docked_ ? window->main_web_contents_ :
506       window->toolbox_web_contents_;
507 }
508 
509 // static
GetInstanceForInspectedWebContents(WebContents * inspected_web_contents)510 DevToolsWindow* DevToolsWindow::GetInstanceForInspectedWebContents(
511     WebContents* inspected_web_contents) {
512   if (!inspected_web_contents || !g_devtools_window_instances.IsCreated())
513     return nullptr;
514   DevToolsWindows* instances = g_devtools_window_instances.Pointer();
515   for (auto it(instances->begin()); it != instances->end(); ++it) {
516     if ((*it)->GetInspectedWebContents() == inspected_web_contents)
517       return *it;
518   }
519   return nullptr;
520 }
521 
522 // static
IsDevToolsWindow(content::WebContents * web_contents)523 bool DevToolsWindow::IsDevToolsWindow(content::WebContents* web_contents) {
524   if (!web_contents || !g_devtools_window_instances.IsCreated())
525     return false;
526   DevToolsWindows* instances = g_devtools_window_instances.Pointer();
527   for (auto it(instances->begin()); it != instances->end(); ++it) {
528     if ((*it)->main_web_contents_ == web_contents ||
529         (*it)->toolbox_web_contents_ == web_contents)
530       return true;
531   }
532   return false;
533 }
534 
535 // static
OpenDevToolsWindowForWorker(Profile * profile,const scoped_refptr<DevToolsAgentHost> & worker_agent)536 void DevToolsWindow::OpenDevToolsWindowForWorker(
537     Profile* profile,
538     const scoped_refptr<DevToolsAgentHost>& worker_agent) {
539   DevToolsWindow* window = FindDevToolsWindow(worker_agent.get());
540   if (!window) {
541     base::RecordAction(base::UserMetricsAction("DevTools_InspectWorker"));
542     window = Create(profile, nullptr, kFrontendWorker, std::string(), false, "",
543                     "", worker_agent->IsAttached());
544     if (!window)
545       return;
546     window->bindings_->AttachTo(worker_agent);
547   }
548   window->ScheduleShow(DevToolsToggleAction::Show());
549 }
550 
551 // static
OpenDevToolsWindow(content::WebContents * inspected_web_contents)552 void DevToolsWindow::OpenDevToolsWindow(
553     content::WebContents* inspected_web_contents) {
554   ToggleDevToolsWindow(
555         inspected_web_contents, true, DevToolsToggleAction::Show(), "");
556 }
557 
558 // static
OpenDevToolsWindow(scoped_refptr<content::DevToolsAgentHost> agent_host,Profile * profile)559 void DevToolsWindow::OpenDevToolsWindow(
560     scoped_refptr<content::DevToolsAgentHost> agent_host,
561     Profile* profile) {
562   OpenDevToolsWindow(agent_host, profile, false /* use_bundled_frontend */);
563 }
564 
565 // static
OpenDevToolsWindowWithBundledFrontend(scoped_refptr<content::DevToolsAgentHost> agent_host,Profile * profile)566 void DevToolsWindow::OpenDevToolsWindowWithBundledFrontend(
567     scoped_refptr<content::DevToolsAgentHost> agent_host,
568     Profile* profile) {
569   OpenDevToolsWindow(agent_host, profile, true /* use_bundled_frontend */);
570 }
571 
572 // static
OpenDevToolsWindow(scoped_refptr<content::DevToolsAgentHost> agent_host,Profile * profile,bool use_bundled_frontend)573 void DevToolsWindow::OpenDevToolsWindow(
574     scoped_refptr<content::DevToolsAgentHost> agent_host,
575     Profile* profile,
576     bool use_bundled_frontend) {
577   if (!profile)
578     profile = Profile::FromBrowserContext(agent_host->GetBrowserContext());
579 
580   if (!profile)
581     return;
582 
583   std::string type = agent_host->GetType();
584 
585   bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
586                    type == DevToolsAgentHost::kTypeSharedWorker;
587 
588   if (!agent_host->GetFrontendURL().empty()) {
589     DevToolsWindow::OpenExternalFrontend(profile, agent_host->GetFrontendURL(),
590                                          agent_host, use_bundled_frontend);
591     return;
592   }
593 
594   if (is_worker) {
595     DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host);
596     return;
597   }
598 
599   if (type == content::DevToolsAgentHost::kTypeFrame) {
600     DevToolsWindow::OpenDevToolsWindowForFrame(profile, agent_host);
601     return;
602   }
603 
604   content::WebContents* web_contents = agent_host->GetWebContents();
605   if (web_contents)
606     DevToolsWindow::OpenDevToolsWindow(web_contents);
607 }
608 
609 // static
OpenDevToolsWindow(content::WebContents * inspected_web_contents,const DevToolsToggleAction & action)610 void DevToolsWindow::OpenDevToolsWindow(
611     content::WebContents* inspected_web_contents,
612     const DevToolsToggleAction& action) {
613   ToggleDevToolsWindow(inspected_web_contents, true, action, "");
614 }
615 
616 // static
OpenDevToolsWindowForFrame(Profile * profile,const scoped_refptr<content::DevToolsAgentHost> & agent_host)617 void DevToolsWindow::OpenDevToolsWindowForFrame(
618     Profile* profile,
619     const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
620   DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
621   if (!window) {
622     window = DevToolsWindow::Create(profile, nullptr, kFrontendDefault,
623                                     std::string(), false, std::string(),
624                                     std::string(), agent_host->IsAttached());
625     if (!window)
626       return;
627     window->bindings_->AttachTo(agent_host);
628   }
629   window->ScheduleShow(DevToolsToggleAction::Show());
630 }
631 
632 // static
ToggleDevToolsWindow(Browser * browser,const DevToolsToggleAction & action,DevToolsOpenedByAction opened_by)633 void DevToolsWindow::ToggleDevToolsWindow(Browser* browser,
634                                           const DevToolsToggleAction& action,
635                                           DevToolsOpenedByAction opened_by) {
636   if (action.type() == DevToolsToggleAction::kToggle &&
637       browser->is_type_devtools()) {
638     browser->tab_strip_model()->CloseAllTabs();
639     return;
640   }
641 
642   ToggleDevToolsWindow(browser->tab_strip_model()->GetActiveWebContents(),
643                        action.type() == DevToolsToggleAction::kInspect, action,
644                        "", opened_by);
645 }
646 
647 // static
OpenExternalFrontend(Profile * profile,const std::string & frontend_url,const scoped_refptr<content::DevToolsAgentHost> & agent_host,bool use_bundled_frontend)648 void DevToolsWindow::OpenExternalFrontend(
649     Profile* profile,
650     const std::string& frontend_url,
651     const scoped_refptr<content::DevToolsAgentHost>& agent_host,
652     bool use_bundled_frontend) {
653   DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
654   if (window) {
655     window->ScheduleShow(DevToolsToggleAction::Show());
656     return;
657   }
658 
659   std::string type = agent_host->GetType();
660   if (type == "node") {
661     // Direct node targets will always open using ToT front-end.
662     window = Create(profile, nullptr, kFrontendV8, std::string(), false,
663                     std::string(), std::string(), agent_host->IsAttached());
664   } else {
665     bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
666                      type == DevToolsAgentHost::kTypeSharedWorker;
667 
668     FrontendType frontend_type =
669         is_worker ? kFrontendRemoteWorker : kFrontendRemote;
670     std::string effective_frontend_url =
671         use_bundled_frontend ? kFallbackFrontendURL
672                              : DevToolsUI::GetProxyURL(frontend_url).spec();
673     window =
674         Create(profile, nullptr, frontend_type, effective_frontend_url, false,
675                std::string(), std::string(), agent_host->IsAttached());
676   }
677   if (!window)
678     return;
679   window->bindings_->AttachTo(agent_host);
680   window->close_on_detach_ = false;
681   window->ScheduleShow(DevToolsToggleAction::Show());
682 }
683 
684 // static
OpenNodeFrontendWindow(Profile * profile)685 DevToolsWindow* DevToolsWindow::OpenNodeFrontendWindow(Profile* profile) {
686   for (DevToolsWindow* window : g_devtools_window_instances.Get()) {
687     if (window->frontend_type_ == kFrontendNode) {
688       window->ActivateWindow();
689       return window;
690     }
691   }
692 
693   DevToolsWindow* window =
694       Create(profile, nullptr, kFrontendNode, std::string(), false,
695              std::string(), std::string(), false);
696   if (!window)
697     return nullptr;
698   window->bindings_->AttachTo(DevToolsAgentHost::CreateForDiscovery());
699   window->ScheduleShow(DevToolsToggleAction::Show());
700   return window;
701 }
702 
703 // static
ToggleDevToolsWindow(content::WebContents * inspected_web_contents,bool force_open,const DevToolsToggleAction & action,const std::string & settings,DevToolsOpenedByAction opened_by)704 void DevToolsWindow::ToggleDevToolsWindow(
705     content::WebContents* inspected_web_contents,
706     bool force_open,
707     const DevToolsToggleAction& action,
708     const std::string& settings,
709     DevToolsOpenedByAction opened_by) {
710   scoped_refptr<DevToolsAgentHost> agent(
711       DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
712   DevToolsWindow* window = FindDevToolsWindow(agent.get());
713   bool do_open = force_open;
714   if (!window) {
715     Profile* profile = Profile::FromBrowserContext(
716         inspected_web_contents->GetBrowserContext());
717     base::RecordAction(base::UserMetricsAction("DevTools_InspectRenderer"));
718     std::string panel;
719     switch (action.type()) {
720       case DevToolsToggleAction::kInspect:
721       case DevToolsToggleAction::kShowElementsPanel:
722         panel = "elements";
723         break;
724       case DevToolsToggleAction::kShowConsolePanel:
725         panel = "console";
726         break;
727       case DevToolsToggleAction::kPauseInDebugger:
728         panel = "sources";
729         break;
730       case DevToolsToggleAction::kShow:
731       case DevToolsToggleAction::kToggle:
732       case DevToolsToggleAction::kReveal:
733       case DevToolsToggleAction::kNoOp:
734         break;
735     }
736     window = Create(profile, inspected_web_contents, kFrontendDefault,
737                     std::string(), true, settings, panel, agent->IsAttached());
738     if (!window)
739       return;
740     window->bindings_->AttachTo(agent.get());
741     do_open = true;
742     if (opened_by != DevToolsOpenedByAction::kUnknown)
743       LogDevToolsOpenedByAction(opened_by);
744   }
745 
746   // Update toolbar to reflect DevTools changes.
747   window->UpdateBrowserToolbar();
748 
749   // If window is docked and visible, we hide it on toggle. If window is
750   // undocked, we show (activate) it.
751   if (!window->is_docked_ || do_open)
752     window->ScheduleShow(action);
753   else
754     window->CloseWindow();
755 }
756 
757 // static
InspectElement(content::RenderFrameHost * inspected_frame_host,int x,int y)758 void DevToolsWindow::InspectElement(
759     content::RenderFrameHost* inspected_frame_host,
760     int x,
761     int y) {
762   WebContents* web_contents =
763       WebContents::FromRenderFrameHost(inspected_frame_host);
764   scoped_refptr<DevToolsAgentHost> agent(
765       DevToolsAgentHost::GetOrCreateFor(web_contents));
766   agent->InspectElement(inspected_frame_host, x, y);
767   bool should_measure_time = !FindDevToolsWindow(agent.get());
768   base::TimeTicks start_time = base::TimeTicks::Now();
769   // TODO(loislo): we should initiate DevTools window opening from within
770   // renderer. Otherwise, we still can hit a race condition here.
771   OpenDevToolsWindow(web_contents, DevToolsToggleAction::ShowElementsPanel());
772   LogDevToolsOpenedByAction(DevToolsOpenedByAction::kContextMenuInspect);
773   DevToolsWindow* window = FindDevToolsWindow(agent.get());
774   if (window && should_measure_time)
775     window->inspect_element_start_time_ = start_time;
776 }
777 
778 // static
LogDevToolsOpenedByAction(DevToolsOpenedByAction opened_by)779 void DevToolsWindow::LogDevToolsOpenedByAction(
780     DevToolsOpenedByAction opened_by) {
781   base::UmaHistogramEnumeration("DevTools.OpenedByAction", opened_by);
782 }
783 
784 // static
785 std::unique_ptr<content::NavigationThrottle>
MaybeCreateNavigationThrottle(content::NavigationHandle * handle)786 DevToolsWindow::MaybeCreateNavigationThrottle(
787     content::NavigationHandle* handle) {
788   WebContents* web_contents = handle->GetWebContents();
789   if (!web_contents || !web_contents->HasOriginalOpener() ||
790       web_contents->GetController().GetLastCommittedEntry()) {
791     return nullptr;
792   }
793 
794   WebContents* opener = WebContents::FromRenderFrameHost(
795       handle->GetWebContents()->GetOriginalOpener());
796   DevToolsWindow* window = GetInstanceForInspectedWebContents(opener);
797   if (!window || !window->open_new_window_for_popups_ ||
798       GetInstanceForInspectedWebContents(web_contents))
799     return nullptr;
800 
801   DevToolsWindow::OpenDevToolsWindow(web_contents);
802   window = GetInstanceForInspectedWebContents(web_contents);
803   if (!window)
804     return nullptr;
805 
806   return std::make_unique<Throttle>(handle, window);
807 }
808 
UpdateInspectedWebContents(content::WebContents * new_web_contents,base::OnceCallback<void ()> callback)809 void DevToolsWindow::UpdateInspectedWebContents(
810     content::WebContents* new_web_contents,
811     base::OnceCallback<void()> callback) {
812   DCHECK(!reattach_complete_callback_);
813   reattach_complete_callback_ = std::move(callback);
814 
815   inspected_contents_observer_ =
816       std::make_unique<ObserverWithAccessor>(new_web_contents);
817   bindings_->AttachTo(
818       content::DevToolsAgentHost::GetOrCreateFor(new_web_contents));
819   bindings_->CallClientMethod(
820       "DevToolsAPI", "reattachMainTarget", {}, {}, {},
821       base::BindOnce(&DevToolsWindow::OnReattachMainTargetComplete,
822                      base::Unretained(this)));
823 }
824 
ScheduleShow(const DevToolsToggleAction & action)825 void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) {
826   if (life_stage_ == kLoadCompleted) {
827     Show(action);
828     return;
829   }
830 
831   // Action will be done only after load completed.
832   action_on_load_ = action;
833 
834   if (!can_dock_) {
835     // No harm to show always-undocked window right away.
836     is_docked_ = false;
837     Show(DevToolsToggleAction::Show());
838   }
839 }
840 
Show(const DevToolsToggleAction & action)841 void DevToolsWindow::Show(const DevToolsToggleAction& action) {
842   if (life_stage_ == kClosing)
843     return;
844 
845   if (action.type() == DevToolsToggleAction::kNoOp)
846     return;
847   if (is_docked_) {
848     DCHECK(can_dock_);
849     Browser* inspected_browser = nullptr;
850     int inspected_tab_index = -1;
851     FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
852                                     &inspected_browser,
853                                     &inspected_tab_index);
854     DCHECK(inspected_browser);
855     DCHECK_NE(-1, inspected_tab_index);
856 
857     RegisterModalDialogManager(inspected_browser);
858 
859     // Tell inspected browser to update splitter and switch to inspected panel.
860     BrowserWindow* inspected_window = inspected_browser->window();
861     main_web_contents_->SetDelegate(this);
862 
863     TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
864     tab_strip_model->ActivateTabAt(inspected_tab_index,
865                                    {TabStripModel::GestureType::kOther});
866 
867     inspected_window->UpdateDevTools();
868     main_web_contents_->SetInitialFocus();
869     inspected_window->Show();
870     // On Aura, focusing once is not enough. Do it again.
871     // Note that focusing only here but not before isn't enough either. We just
872     // need to focus twice.
873     main_web_contents_->SetInitialFocus();
874 
875     PrefsTabHelper::CreateForWebContents(main_web_contents_);
876     OverrideAndSyncDevToolsRendererPrefs();
877 
878     DoAction(action);
879     return;
880   }
881 
882   // Avoid consecutive window switching if the devtools window has been opened
883   // and the Inspect Element shortcut is pressed in the inspected tab.
884   bool should_show_window =
885       !browser_ || (action.type() != DevToolsToggleAction::kInspect);
886 
887   if (!browser_)
888     CreateDevToolsBrowser();
889 
890   RegisterModalDialogManager(browser_);
891 
892   if (should_show_window) {
893     browser_->window()->Show();
894     main_web_contents_->SetInitialFocus();
895   }
896   if (toolbox_web_contents_)
897     UpdateBrowserWindow();
898 
899   DoAction(action);
900 }
901 
902 // static
HandleBeforeUnload(WebContents * frontend_contents,bool proceed,bool * proceed_to_fire_unload)903 bool DevToolsWindow::HandleBeforeUnload(WebContents* frontend_contents,
904     bool proceed, bool* proceed_to_fire_unload) {
905   DevToolsWindow* window = AsDevToolsWindow(frontend_contents);
906   if (!window)
907     return false;
908   if (!window->intercepted_page_beforeunload_)
909     return false;
910   window->BeforeUnloadFired(frontend_contents, proceed,
911       proceed_to_fire_unload);
912   return true;
913 }
914 
915 // static
InterceptPageBeforeUnload(WebContents * contents)916 bool DevToolsWindow::InterceptPageBeforeUnload(WebContents* contents) {
917   DevToolsWindow* window =
918       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
919   if (!window || window->intercepted_page_beforeunload_)
920     return false;
921 
922   // Not yet loaded frontend will not handle beforeunload.
923   if (window->life_stage_ != kLoadCompleted)
924     return false;
925 
926   window->intercepted_page_beforeunload_ = true;
927   // Handle case of devtools inspecting another devtools instance by passing
928   // the call up to the inspecting devtools instance.
929   // TODO(chrisha): Make devtools handle |auto_cancel=false| unload handler
930   // dispatches; otherwise, discarding queries can cause unload dialogs to
931   // pop-up for tabs with an attached devtools.
932   if (!DevToolsWindow::InterceptPageBeforeUnload(window->main_web_contents_)) {
933     window->main_web_contents_->DispatchBeforeUnload(false /* auto_cancel */);
934   }
935   return true;
936 }
937 
938 // static
NeedsToInterceptBeforeUnload(WebContents * contents)939 bool DevToolsWindow::NeedsToInterceptBeforeUnload(
940     WebContents* contents) {
941   DevToolsWindow* window =
942       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
943   return window && !window->intercepted_page_beforeunload_ &&
944          window->life_stage_ == kLoadCompleted;
945 }
946 
947 // static
HasFiredBeforeUnloadEventForDevToolsBrowser(Browser * browser)948 bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
949     Browser* browser) {
950   DCHECK(browser->is_type_devtools());
951   // When FastUnloadController is used, devtools frontend will be detached
952   // from the browser window at this point which means we've already fired
953   // beforeunload.
954   if (browser->tab_strip_model()->empty())
955     return true;
956   DevToolsWindow* window = AsDevToolsWindow(browser);
957   if (!window)
958     return false;
959   return window->intercepted_page_beforeunload_;
960 }
961 
962 // static
OnPageCloseCanceled(WebContents * contents)963 void DevToolsWindow::OnPageCloseCanceled(WebContents* contents) {
964   DevToolsWindow* window =
965       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
966   if (!window)
967     return;
968   window->intercepted_page_beforeunload_ = false;
969   // Propagate to devtools opened on devtools if any.
970   DevToolsWindow::OnPageCloseCanceled(window->main_web_contents_);
971 }
972 
DevToolsWindow(FrontendType frontend_type,Profile * profile,std::unique_ptr<WebContents> main_web_contents,DevToolsUIBindings * bindings,WebContents * inspected_web_contents,bool can_dock)973 DevToolsWindow::DevToolsWindow(FrontendType frontend_type,
974                                Profile* profile,
975                                std::unique_ptr<WebContents> main_web_contents,
976                                DevToolsUIBindings* bindings,
977                                WebContents* inspected_web_contents,
978                                bool can_dock)
979     : frontend_type_(frontend_type),
980       profile_(profile),
981       main_web_contents_(main_web_contents.get()),
982       toolbox_web_contents_(nullptr),
983       bindings_(bindings),
984       browser_(nullptr),
985       is_docked_(true),
986       owned_main_web_contents_(
987           std::make_unique<OwnedMainWebContents>(std::move(main_web_contents))),
988       can_dock_(can_dock),
989       close_on_detach_(true),
990       // This initialization allows external front-end to work without changes.
991       // We don't wait for docking call, but instead immediately show undocked.
992       // Passing "dockSide=undocked" parameter ensures proper UI.
993       life_stage_(can_dock ? kNotLoaded : kIsDockedSet),
994       action_on_load_(DevToolsToggleAction::NoOp()),
995       intercepted_page_beforeunload_(false),
996       ready_for_test_(false) {
997   // Set up delegate, so we get fully-functional window immediately.
998   // It will not appear in UI though until |life_stage_ == kLoadCompleted|.
999   main_web_contents_->SetDelegate(this);
1000   // Bindings take ownership over devtools as its delegate.
1001   bindings_->SetDelegate(this);
1002   // DevTools uses PageZoom::Zoom(), so main_web_contents_ requires a
1003   // ZoomController.
1004   zoom::ZoomController::CreateForWebContents(main_web_contents_);
1005   zoom::ZoomController::FromWebContents(main_web_contents_)
1006       ->SetShowsNotificationBubble(false);
1007 
1008   g_devtools_window_instances.Get().push_back(this);
1009 
1010   // There is no inspected_web_contents in case of various workers.
1011   if (inspected_web_contents)
1012     inspected_contents_observer_.reset(
1013         new ObserverWithAccessor(inspected_web_contents));
1014 
1015   // Initialize docked page to be of the right size.
1016   if (can_dock_ && inspected_web_contents) {
1017     content::RenderWidgetHostView* inspected_view =
1018         inspected_web_contents->GetRenderWidgetHostView();
1019     if (inspected_view && main_web_contents_->GetRenderWidgetHostView()) {
1020       gfx::Size size = inspected_view->GetViewBounds().size();
1021       main_web_contents_->GetRenderWidgetHostView()->SetSize(size);
1022     }
1023   }
1024 
1025   event_forwarder_.reset(new DevToolsEventForwarder(this));
1026 
1027   // Tag the DevTools main WebContents with its TaskManager specific UserData
1028   // so that it shows up in the task manager.
1029   task_manager::WebContentsTags::CreateForDevToolsContents(main_web_contents_);
1030 
1031   std::vector<base::Callback<void(DevToolsWindow*)>> copy(
1032       g_creation_callbacks.Get());
1033   for (const auto& callback : copy)
1034     callback.Run(this);
1035 
1036   pref_change_registrar_.Init(profile_->GetPrefs());
1037   pref_change_registrar_.Add(
1038       language::prefs::kAcceptLanguages,
1039       base::BindRepeating(&DevToolsWindow::OnLocaleChanged,
1040                           base::Unretained(this)));
1041 }
1042 
1043 // static
AllowDevToolsFor(Profile * profile,content::WebContents * web_contents)1044 bool DevToolsWindow::AllowDevToolsFor(Profile* profile,
1045                                       content::WebContents* web_contents) {
1046   // Don't allow DevTools UI in kiosk mode, because the DevTools UI would be
1047   // broken there. See https://crbug.com/514551 for context.
1048   if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
1049     return false;
1050 
1051   return ChromeDevToolsManagerDelegate::AllowInspection(profile, web_contents);
1052 }
1053 
1054 // static
Create(Profile * profile,content::WebContents * inspected_web_contents,FrontendType frontend_type,const std::string & frontend_url,bool can_dock,const std::string & settings,const std::string & panel,bool has_other_clients)1055 DevToolsWindow* DevToolsWindow::Create(
1056     Profile* profile,
1057     content::WebContents* inspected_web_contents,
1058     FrontendType frontend_type,
1059     const std::string& frontend_url,
1060     bool can_dock,
1061     const std::string& settings,
1062     const std::string& panel,
1063     bool has_other_clients) {
1064   if (!AllowDevToolsFor(profile, inspected_web_contents))
1065     return nullptr;
1066 
1067   if (inspected_web_contents) {
1068     // Check for a place to dock.
1069     Browser* browser = nullptr;
1070     int tab;
1071     if (!FindInspectedBrowserAndTabIndex(inspected_web_contents, &browser,
1072                                          &tab) ||
1073         !browser->is_type_normal()) {
1074       can_dock = false;
1075     }
1076   }
1077 
1078   // Create WebContents with devtools.
1079   GURL url(GetDevToolsURL(profile, frontend_type, frontend_url, can_dock, panel,
1080                           has_other_clients));
1081   std::unique_ptr<WebContents> main_web_contents =
1082       WebContents::Create(WebContents::CreateParams(profile));
1083   main_web_contents->GetController().LoadURL(
1084       DecorateFrontendURL(url), content::Referrer(),
1085       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
1086   DevToolsUIBindings* bindings =
1087       DevToolsUIBindings::ForWebContents(main_web_contents.get());
1088 
1089   if (!bindings)
1090     return nullptr;
1091   if (!settings.empty())
1092     SetPreferencesFromJson(profile, settings);
1093   return new DevToolsWindow(frontend_type, profile,
1094                             std::move(main_web_contents), bindings,
1095                             inspected_web_contents, can_dock);
1096 }
1097 
1098 // static
GetDevToolsURL(Profile * profile,FrontendType frontend_type,const std::string & frontend_url,bool can_dock,const std::string & panel,bool has_other_clients)1099 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
1100                                     FrontendType frontend_type,
1101                                     const std::string& frontend_url,
1102                                     bool can_dock,
1103                                     const std::string& panel,
1104                                     bool has_other_clients) {
1105   std::string url;
1106 
1107   std::string remote_base =
1108       "?remoteBase=" + DevToolsUI::GetRemoteBaseURL().spec();
1109 
1110   const std::string valid_frontend =
1111       frontend_url.empty() ? chrome::kChromeUIDevToolsURL : frontend_url;
1112 
1113   // remoteFrontend is here for backwards compatibility only.
1114   std::string remote_frontend =
1115       valid_frontend + ((valid_frontend.find("?") == std::string::npos)
1116                             ? "?remoteFrontend=true"
1117                             : "&remoteFrontend=true");
1118   switch (frontend_type) {
1119     case kFrontendDefault:
1120       url = kDefaultFrontendURL + remote_base;
1121       if (can_dock)
1122         url += "&can_dock=true";
1123       if (!panel.empty())
1124         url += "&panel=" + panel;
1125       break;
1126     case kFrontendWorker:
1127       url = kWorkerFrontendURL + remote_base;
1128       break;
1129     case kFrontendV8:
1130       url = kJSFrontendURL + remote_base;
1131       break;
1132     case kFrontendNode:
1133       url = kNodeFrontendURL + remote_base;
1134       break;
1135     case kFrontendRemote:
1136       url = remote_frontend;
1137       break;
1138     case kFrontendRemoteWorker:
1139       // isSharedWorker is here for backwards compatibility only.
1140       url = remote_frontend + "&isSharedWorker=true";
1141       break;
1142   }
1143 
1144   if (has_other_clients)
1145     url += "&hasOtherClients=true";
1146   return DevToolsUIBindings::SanitizeFrontendURL(GURL(url));
1147 }
1148 
1149 // static
FindDevToolsWindow(DevToolsAgentHost * agent_host)1150 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
1151     DevToolsAgentHost* agent_host) {
1152   if (!agent_host || !g_devtools_window_instances.IsCreated())
1153     return nullptr;
1154   DevToolsWindows* instances = g_devtools_window_instances.Pointer();
1155   for (auto it(instances->begin()); it != instances->end(); ++it) {
1156     if ((*it)->bindings_->IsAttachedTo(agent_host))
1157       return *it;
1158   }
1159   return nullptr;
1160 }
1161 
1162 // static
AsDevToolsWindow(content::WebContents * web_contents)1163 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
1164     content::WebContents* web_contents) {
1165   if (!web_contents || !g_devtools_window_instances.IsCreated())
1166     return nullptr;
1167   DevToolsWindows* instances = g_devtools_window_instances.Pointer();
1168   for (auto it(instances->begin()); it != instances->end(); ++it) {
1169     if ((*it)->main_web_contents_ == web_contents)
1170       return *it;
1171   }
1172   return nullptr;
1173 }
1174 
1175 // static
AsDevToolsWindow(Browser * browser)1176 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(Browser* browser) {
1177   DCHECK(browser->is_type_devtools());
1178   if (browser->tab_strip_model()->empty())
1179     return nullptr;
1180   WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(0);
1181   return AsDevToolsWindow(contents);
1182 }
1183 
OpenURLFromTab(WebContents * source,const content::OpenURLParams & params)1184 WebContents* DevToolsWindow::OpenURLFromTab(
1185     WebContents* source,
1186     const content::OpenURLParams& params) {
1187   DCHECK(source == main_web_contents_);
1188   if (!params.url.SchemeIs(content::kChromeDevToolsScheme)) {
1189     return OpenURLFromInspectedTab(params);
1190   }
1191   main_web_contents_->GetController().Reload(content::ReloadType::NORMAL,
1192                                              false);
1193   return main_web_contents_;
1194 }
1195 
OpenURLFromInspectedTab(const content::OpenURLParams & params)1196 WebContents* DevToolsWindow::OpenURLFromInspectedTab(
1197     const content::OpenURLParams& params) {
1198   WebContents* inspected_web_contents = GetInspectedWebContents();
1199   if (!inspected_web_contents)
1200     return nullptr;
1201   content::OpenURLParams modified = params;
1202   modified.referrer = content::Referrer();
1203   return inspected_web_contents->OpenURL(modified);
1204 }
1205 
ActivateContents(WebContents * contents)1206 void DevToolsWindow::ActivateContents(WebContents* contents) {
1207   if (is_docked_) {
1208     WebContents* inspected_tab = GetInspectedWebContents();
1209     if (inspected_tab)
1210       inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
1211   } else if (browser_) {
1212     browser_->window()->Activate();
1213   }
1214 }
1215 
AddNewContents(WebContents * source,std::unique_ptr<WebContents> new_contents,const GURL & target_url,WindowOpenDisposition disposition,const gfx::Rect & initial_rect,bool user_gesture,bool * was_blocked)1216 void DevToolsWindow::AddNewContents(WebContents* source,
1217                                     std::unique_ptr<WebContents> new_contents,
1218                                     const GURL& target_url,
1219                                     WindowOpenDisposition disposition,
1220                                     const gfx::Rect& initial_rect,
1221                                     bool user_gesture,
1222                                     bool* was_blocked) {
1223   if (new_contents.get() == toolbox_web_contents_) {
1224     owned_toolbox_web_contents_ = std::move(new_contents);
1225 
1226     toolbox_web_contents_->SetDelegate(
1227         new DevToolsToolboxDelegate(toolbox_web_contents_,
1228                                     inspected_contents_observer_.get()));
1229     if (main_web_contents_->GetRenderWidgetHostView() &&
1230         toolbox_web_contents_->GetRenderWidgetHostView()) {
1231       gfx::Size size =
1232           main_web_contents_->GetRenderWidgetHostView()->GetViewBounds().size();
1233       toolbox_web_contents_->GetRenderWidgetHostView()->SetSize(size);
1234     }
1235     UpdateBrowserWindow();
1236     return;
1237   }
1238 
1239   WebContents* inspected_web_contents = GetInspectedWebContents();
1240   if (inspected_web_contents) {
1241     inspected_web_contents->GetDelegate()->AddNewContents(
1242         source, std::move(new_contents), target_url, disposition, initial_rect,
1243         user_gesture, was_blocked);
1244   }
1245 }
1246 
WebContentsCreated(WebContents * source_contents,int opener_render_process_id,int opener_render_frame_id,const std::string & frame_name,const GURL & target_url,WebContents * new_contents)1247 void DevToolsWindow::WebContentsCreated(WebContents* source_contents,
1248                                         int opener_render_process_id,
1249                                         int opener_render_frame_id,
1250                                         const std::string& frame_name,
1251                                         const GURL& target_url,
1252                                         WebContents* new_contents) {
1253   if (target_url.SchemeIs(content::kChromeDevToolsScheme) &&
1254       target_url.path().rfind("toolbox.html") != std::string::npos) {
1255     CHECK(can_dock_);
1256 
1257     // Ownership will be passed in DevToolsWindow::AddNewContents.
1258     if (owned_toolbox_web_contents_)
1259       owned_toolbox_web_contents_.reset();
1260     toolbox_web_contents_ = new_contents;
1261 
1262     // Tag the DevTools toolbox WebContents with its TaskManager specific
1263     // UserData so that it shows up in the task manager.
1264     task_manager::WebContentsTags::CreateForDevToolsContents(
1265         toolbox_web_contents_);
1266 
1267     // The toolbox holds a placeholder for the inspected WebContents. When the
1268     // placeholder is resized, a frame is requested. The inspected WebContents
1269     // is resized when the frame is rendered. Force rendering of the toolbox at
1270     // all times, to make sure that a frame can be rendered even when the
1271     // inspected WebContents fully covers the toolbox. https://crbug.com/828307
1272     toolbox_web_contents_->IncrementCapturerCount(gfx::Size(),
1273                                                   /* stay_hidden */ false);
1274   }
1275 }
1276 
CloseContents(WebContents * source)1277 void DevToolsWindow::CloseContents(WebContents* source) {
1278   CHECK(is_docked_);
1279   life_stage_ = kClosing;
1280   UpdateBrowserWindow();
1281   // In case of docked main_web_contents_, we own it so delete here.
1282   // Embedding DevTools window will be deleted as a result of
1283   // DevToolsUIBindings destruction.
1284   CHECK(owned_main_web_contents_);
1285   owned_main_web_contents_.reset();
1286 }
1287 
ContentsZoomChange(bool zoom_in)1288 void DevToolsWindow::ContentsZoomChange(bool zoom_in) {
1289   DCHECK(is_docked_);
1290   zoom::PageZoom::Zoom(main_web_contents_, zoom_in ? content::PAGE_ZOOM_IN
1291                                                    : content::PAGE_ZOOM_OUT);
1292 }
1293 
BeforeUnloadFired(WebContents * tab,bool proceed,bool * proceed_to_fire_unload)1294 void DevToolsWindow::BeforeUnloadFired(WebContents* tab,
1295                                        bool proceed,
1296                                        bool* proceed_to_fire_unload) {
1297   if (!intercepted_page_beforeunload_) {
1298     // Docked devtools window closed directly.
1299     if (proceed)
1300       bindings_->Detach();
1301     *proceed_to_fire_unload = proceed;
1302   } else {
1303     // Inspected page is attempting to close.
1304     WebContents* inspected_web_contents = GetInspectedWebContents();
1305     if (proceed) {
1306       inspected_web_contents->DispatchBeforeUnload(false /* auto_cancel */);
1307     } else {
1308       bool should_proceed;
1309       inspected_web_contents->GetDelegate()->BeforeUnloadFired(
1310           inspected_web_contents, false, &should_proceed);
1311       DCHECK(!should_proceed);
1312     }
1313     *proceed_to_fire_unload = false;
1314   }
1315 }
1316 
PreHandleKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event)1317 content::KeyboardEventProcessingResult DevToolsWindow::PreHandleKeyboardEvent(
1318     WebContents* source,
1319     const content::NativeWebKeyboardEvent& event) {
1320   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1321   if (inspected_window) {
1322     return inspected_window->PreHandleKeyboardEvent(event);
1323   }
1324   return content::KeyboardEventProcessingResult::NOT_HANDLED;
1325 }
1326 
HandleKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event)1327 bool DevToolsWindow::HandleKeyboardEvent(
1328     WebContents* source,
1329     const content::NativeWebKeyboardEvent& event) {
1330   if (event.windows_key_code == 0x08) {
1331     // Do not navigate back in history on Windows (http://crbug.com/74156).
1332     return true;
1333   }
1334   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1335   return inspected_window && inspected_window->HandleKeyboardEvent(event);
1336 }
1337 
GetJavaScriptDialogManager(WebContents * source)1338 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager(
1339     WebContents* source) {
1340   return javascript_dialogs::AppModalDialogManager::GetInstance();
1341 }
1342 
OpenColorChooser(WebContents * web_contents,SkColor initial_color,const std::vector<blink::mojom::ColorSuggestionPtr> & suggestions)1343 content::ColorChooser* DevToolsWindow::OpenColorChooser(
1344     WebContents* web_contents,
1345     SkColor initial_color,
1346     const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
1347   return chrome::ShowColorChooser(web_contents, initial_color);
1348 }
1349 
RunFileChooser(content::RenderFrameHost * render_frame_host,scoped_refptr<content::FileSelectListener> listener,const blink::mojom::FileChooserParams & params)1350 void DevToolsWindow::RunFileChooser(
1351     content::RenderFrameHost* render_frame_host,
1352     scoped_refptr<content::FileSelectListener> listener,
1353     const blink::mojom::FileChooserParams& params) {
1354   FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener),
1355                                    params);
1356 }
1357 
PreHandleGestureEvent(WebContents * source,const blink::WebGestureEvent & event)1358 bool DevToolsWindow::PreHandleGestureEvent(
1359     WebContents* source,
1360     const blink::WebGestureEvent& event) {
1361   // Disable pinch zooming.
1362   return blink::WebInputEvent::IsPinchGestureEventType(event.GetType());
1363 }
1364 
ActivateWindow()1365 void DevToolsWindow::ActivateWindow() {
1366   if (life_stage_ != kLoadCompleted)
1367     return;
1368   if (is_docked_ && GetInspectedBrowserWindow())
1369     main_web_contents_->Focus();
1370   else if (!is_docked_ && !browser_->window()->IsActive())
1371     browser_->window()->Activate();
1372 }
1373 
CloseWindow()1374 void DevToolsWindow::CloseWindow() {
1375   DCHECK(is_docked_);
1376   life_stage_ = kClosing;
1377   main_web_contents_->DispatchBeforeUnload(false /* auto_cancel */);
1378 }
1379 
Inspect(scoped_refptr<content::DevToolsAgentHost> host)1380 void DevToolsWindow::Inspect(scoped_refptr<content::DevToolsAgentHost> host) {
1381   DevToolsWindow::OpenDevToolsWindow(host, profile_);
1382 }
1383 
SetInspectedPageBounds(const gfx::Rect & rect)1384 void DevToolsWindow::SetInspectedPageBounds(const gfx::Rect& rect) {
1385   DevToolsContentsResizingStrategy strategy(rect);
1386   if (contents_resizing_strategy_.Equals(strategy))
1387     return;
1388 
1389   contents_resizing_strategy_.CopyFrom(strategy);
1390   UpdateBrowserWindow();
1391 }
1392 
InspectElementCompleted()1393 void DevToolsWindow::InspectElementCompleted() {
1394   if (!inspect_element_start_time_.is_null()) {
1395     UMA_HISTOGRAM_TIMES("DevTools.InspectElement",
1396         base::TimeTicks::Now() - inspect_element_start_time_);
1397     inspect_element_start_time_ = base::TimeTicks();
1398   }
1399 }
1400 
SetIsDocked(bool dock_requested)1401 void DevToolsWindow::SetIsDocked(bool dock_requested) {
1402   if (life_stage_ == kClosing)
1403     return;
1404 
1405   DCHECK(can_dock_ || !dock_requested);
1406   if (!can_dock_)
1407     dock_requested = false;
1408 
1409   bool was_docked = is_docked_;
1410   is_docked_ = dock_requested;
1411 
1412   if (life_stage_ != kLoadCompleted) {
1413     // This is a first time call we waited for to initialize.
1414     life_stage_ = life_stage_ == kOnLoadFired ? kLoadCompleted : kIsDockedSet;
1415     if (life_stage_ == kLoadCompleted)
1416       LoadCompleted();
1417     return;
1418   }
1419 
1420   if (dock_requested == was_docked)
1421     return;
1422 
1423   if (dock_requested && !was_docked) {
1424     // Detach window from the external devtools browser. It will lead to
1425     // the browser object's close and delete. Remove observer first.
1426     TabStripModel* tab_strip_model = browser_->tab_strip_model();
1427     DCHECK(!owned_main_web_contents_);
1428 
1429     // Removing the only WebContents from the tab strip of browser_ will
1430     // eventually lead to the destruction of browser_ as well, which is why it's
1431     // okay to just null the raw pointer here.
1432     browser_ = nullptr;
1433 
1434     owned_main_web_contents_ = std::make_unique<OwnedMainWebContents>(
1435         tab_strip_model->DetachWebContentsAt(
1436             tab_strip_model->GetIndexOfWebContents(main_web_contents_)));
1437   } else if (!dock_requested && was_docked) {
1438     UpdateBrowserWindow();
1439   }
1440 
1441   Show(DevToolsToggleAction::Show());
1442 }
1443 
OpenInNewTab(const std::string & url)1444 void DevToolsWindow::OpenInNewTab(const std::string& url) {
1445   GURL fixed_url(url);
1446   WebContents* inspected_web_contents = GetInspectedWebContents();
1447   int child_id = content::ChildProcessHost::kInvalidUniqueID;
1448   if (inspected_web_contents) {
1449     content::RenderViewHost* render_view_host =
1450         inspected_web_contents->GetMainFrame()->GetRenderViewHost();
1451     if (render_view_host)
1452       child_id = render_view_host->GetProcess()->GetID();
1453   }
1454   // Use about:blank instead of an empty GURL. The browser treats an empty GURL
1455   // as navigating to the home page, which may be privileged (chrome://newtab/).
1456   if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL(
1457           child_id, fixed_url))
1458     fixed_url = GURL(url::kAboutBlankURL);
1459 
1460   content::OpenURLParams params(fixed_url, content::Referrer(),
1461                                 WindowOpenDisposition::NEW_FOREGROUND_TAB,
1462                                 ui::PAGE_TRANSITION_LINK, false);
1463   if (!inspected_web_contents || !inspected_web_contents->OpenURL(params)) {
1464     chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
1465     chrome::AddSelectedTabWithURL(displayer.browser(), fixed_url,
1466                                   ui::PAGE_TRANSITION_LINK);
1467   }
1468 }
1469 
SetWhitelistedShortcuts(const std::string & message)1470 void DevToolsWindow::SetWhitelistedShortcuts(
1471     const std::string& message) {
1472   event_forwarder_->SetWhitelistedShortcuts(message);
1473 }
1474 
SetEyeDropperActive(bool active)1475 void DevToolsWindow::SetEyeDropperActive(bool active) {
1476   WebContents* web_contents = GetInspectedWebContents();
1477   if (!web_contents)
1478     return;
1479   if (active) {
1480     eye_dropper_.reset(new DevToolsEyeDropper(
1481         web_contents, base::Bind(&DevToolsWindow::ColorPickedInEyeDropper,
1482                                  base::Unretained(this))));
1483   } else {
1484     eye_dropper_.reset();
1485   }
1486 }
1487 
ColorPickedInEyeDropper(int r,int g,int b,int a)1488 void DevToolsWindow::ColorPickedInEyeDropper(int r, int g, int b, int a) {
1489   base::DictionaryValue color;
1490   color.SetInteger("r", r);
1491   color.SetInteger("g", g);
1492   color.SetInteger("b", b);
1493   color.SetInteger("a", a);
1494   bindings_->CallClientMethod("DevToolsAPI", "eyeDropperPickedColor",
1495                               std::move(color));
1496 }
1497 
InspectedContentsClosing()1498 void DevToolsWindow::InspectedContentsClosing() {
1499   if (!close_on_detach_)
1500     return;
1501   intercepted_page_beforeunload_ = false;
1502   life_stage_ = kClosing;
1503   main_web_contents_->ClosePage();
1504 }
1505 
GetInfoBarService()1506 InfoBarService* DevToolsWindow::GetInfoBarService() {
1507   return is_docked_ ?
1508       InfoBarService::FromWebContents(GetInspectedWebContents()) :
1509       InfoBarService::FromWebContents(main_web_contents_);
1510 }
1511 
RenderProcessGone(bool crashed)1512 void DevToolsWindow::RenderProcessGone(bool crashed) {
1513   // Docked DevToolsWindow owns its main_web_contents_ and must delete it.
1514   // Undocked main_web_contents_ are owned and handled by browser.
1515   // see crbug.com/369932
1516   if (is_docked_) {
1517     CloseContents(main_web_contents_);
1518   } else if (browser_ && crashed) {
1519     browser_->window()->Close();
1520   }
1521 }
1522 
ShowCertificateViewer(const std::string & cert_chain)1523 void DevToolsWindow::ShowCertificateViewer(const std::string& cert_chain) {
1524   base::Optional<base::Value> value = base::JSONReader::Read(cert_chain);
1525   CHECK(value && value->is_list());
1526   std::vector<std::string> decoded;
1527   for (const auto& item : value->GetList()) {
1528     CHECK(item.is_string());
1529     std::string temp;
1530     CHECK(base::Base64Decode(item.GetString(), &temp));
1531     decoded.push_back(std::move(temp));
1532   }
1533 
1534   std::vector<base::StringPiece> cert_string_piece;
1535   for (const auto& str : decoded)
1536     cert_string_piece.push_back(str);
1537   scoped_refptr<net::X509Certificate> cert =
1538       net::X509Certificate::CreateFromDERCertChain(cert_string_piece);
1539   CHECK(cert);
1540 
1541   WebContents* inspected_contents =
1542       is_docked_ ? GetInspectedWebContents() : main_web_contents_;
1543   Browser* browser = nullptr;
1544   int tab = 0;
1545   if (!FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
1546     return;
1547   gfx::NativeWindow parent = browser->window()->GetNativeWindow();
1548   ::ShowCertificateViewer(inspected_contents, parent, cert.get());
1549 }
1550 
OnLoadCompleted()1551 void DevToolsWindow::OnLoadCompleted() {
1552   // First seed inspected tab id for extension APIs.
1553   WebContents* inspected_web_contents = GetInspectedWebContents();
1554   if (inspected_web_contents) {
1555     sessions::SessionTabHelper* session_tab_helper =
1556         sessions::SessionTabHelper::FromWebContents(inspected_web_contents);
1557     if (session_tab_helper) {
1558       bindings_->CallClientMethod(
1559           "DevToolsAPI", "setInspectedTabId",
1560           base::Value(session_tab_helper->session_id().id()));
1561     }
1562   }
1563 
1564   if (life_stage_ == kClosing)
1565     return;
1566 
1567   // We could be in kLoadCompleted state already if frontend reloads itself.
1568   if (life_stage_ != kLoadCompleted) {
1569     // Load is completed when both kIsDockedSet and kOnLoadFired happened.
1570     // Here we set kOnLoadFired.
1571     life_stage_ = life_stage_ == kIsDockedSet ? kLoadCompleted : kOnLoadFired;
1572   }
1573   if (life_stage_ == kLoadCompleted)
1574     LoadCompleted();
1575 }
1576 
ReadyForTest()1577 void DevToolsWindow::ReadyForTest() {
1578   ready_for_test_ = true;
1579   if (!ready_for_test_callback_.is_null())
1580     std::move(ready_for_test_callback_).Run();
1581 }
1582 
ConnectionReady()1583 void DevToolsWindow::ConnectionReady() {
1584   if (throttle_)
1585     throttle_->ResumeThrottle();
1586 }
1587 
SetOpenNewWindowForPopups(bool value)1588 void DevToolsWindow::SetOpenNewWindowForPopups(bool value) {
1589   open_new_window_for_popups_ = value;
1590 }
1591 
CreateDevToolsBrowser()1592 void DevToolsWindow::CreateDevToolsBrowser() {
1593   PrefService* prefs = profile_->GetPrefs();
1594   if (!prefs->GetDictionary(prefs::kAppWindowPlacement)->HasKey(kDevToolsApp)) {
1595     // Ensure there is always a default size so that
1596     // BrowserFrame::InitBrowserFrame can retrieve it later.
1597     DictionaryPrefUpdate update(prefs, prefs::kAppWindowPlacement);
1598     base::Value* wp_prefs = update.Get();
1599     base::Value dev_tools_defaults(base::Value::Type::DICTIONARY);
1600     dev_tools_defaults.SetIntKey("left", 100);
1601     dev_tools_defaults.SetIntKey("top", 100);
1602     dev_tools_defaults.SetIntKey("right", 740);
1603     dev_tools_defaults.SetIntKey("bottom", 740);
1604     dev_tools_defaults.SetBoolKey("maximized", false);
1605     dev_tools_defaults.SetBoolKey("always_on_top", false);
1606     wp_prefs->SetKey(kDevToolsApp, std::move(dev_tools_defaults));
1607   }
1608 
1609   browser_ =
1610       Browser::Create(Browser::CreateParams::CreateForDevTools(profile_));
1611   browser_->tab_strip_model()->AddWebContents(
1612       OwnedMainWebContents::TakeWebContents(
1613           std::move(owned_main_web_contents_)),
1614       -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL, TabStripModel::ADD_ACTIVE);
1615   OverrideAndSyncDevToolsRendererPrefs();
1616 }
1617 
GetInspectedBrowserWindow()1618 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
1619   Browser* browser = nullptr;
1620   int tab;
1621   return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(), &browser,
1622                                          &tab)
1623              ? browser->window()
1624              : nullptr;
1625 }
1626 
DoAction(const DevToolsToggleAction & action)1627 void DevToolsWindow::DoAction(const DevToolsToggleAction& action) {
1628   switch (action.type()) {
1629     case DevToolsToggleAction::kInspect:
1630       bindings_->CallClientMethod("DevToolsAPI", "enterInspectElementMode");
1631       break;
1632 
1633     case DevToolsToggleAction::kShowElementsPanel:
1634     case DevToolsToggleAction::kPauseInDebugger:
1635     case DevToolsToggleAction::kShowConsolePanel:
1636     case DevToolsToggleAction::kShow:
1637     case DevToolsToggleAction::kToggle:
1638       // Do nothing.
1639       break;
1640 
1641     case DevToolsToggleAction::kReveal: {
1642       const DevToolsToggleAction::RevealParams* params =
1643           action.params();
1644       CHECK(params);
1645       bindings_->CallClientMethod(
1646           "DevToolsAPI", "revealSourceLine", base::Value(params->url),
1647           base::Value(static_cast<int>(params->line_number)),
1648           base::Value(static_cast<int>(params->column_number)));
1649       break;
1650     }
1651     default:
1652       NOTREACHED();
1653       break;
1654   }
1655 }
1656 
UpdateBrowserToolbar()1657 void DevToolsWindow::UpdateBrowserToolbar() {
1658   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1659   if (inspected_window)
1660     inspected_window->UpdateToolbar(nullptr);
1661 }
1662 
UpdateBrowserWindow()1663 void DevToolsWindow::UpdateBrowserWindow() {
1664   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1665   if (inspected_window)
1666     inspected_window->UpdateDevTools();
1667 }
1668 
GetInspectedWebContents()1669 WebContents* DevToolsWindow::GetInspectedWebContents() {
1670   return inspected_contents_observer_
1671              ? inspected_contents_observer_->web_contents()
1672              : nullptr;
1673 }
1674 
LoadCompleted()1675 void DevToolsWindow::LoadCompleted() {
1676   Show(action_on_load_);
1677   action_on_load_ = DevToolsToggleAction::NoOp();
1678   if (!load_completed_callback_.is_null()) {
1679     std::move(load_completed_callback_).Run();
1680   }
1681 }
1682 
SetLoadCompletedCallback(base::OnceClosure closure)1683 void DevToolsWindow::SetLoadCompletedCallback(base::OnceClosure closure) {
1684   if (life_stage_ == kLoadCompleted || life_stage_ == kClosing) {
1685     if (!closure.is_null())
1686       std::move(closure).Run();
1687     return;
1688   }
1689   load_completed_callback_ = std::move(closure);
1690 }
1691 
ForwardKeyboardEvent(const content::NativeWebKeyboardEvent & event)1692 bool DevToolsWindow::ForwardKeyboardEvent(
1693     const content::NativeWebKeyboardEvent& event) {
1694   return event_forwarder_->ForwardEvent(event);
1695 }
1696 
ReloadInspectedWebContents(bool bypass_cache)1697 bool DevToolsWindow::ReloadInspectedWebContents(bool bypass_cache) {
1698   // Only route reload via front-end if the agent is attached.
1699   WebContents* wc = GetInspectedWebContents();
1700   if (!wc || wc->GetCrashedStatus() != base::TERMINATION_STATUS_STILL_RUNNING)
1701     return false;
1702   bindings_->CallClientMethod("DevToolsAPI", "reloadInspectedPage",
1703                               base::Value(bypass_cache));
1704   return true;
1705 }
1706 
RegisterModalDialogManager(Browser * browser)1707 void DevToolsWindow::RegisterModalDialogManager(Browser* browser) {
1708   web_modal::WebContentsModalDialogManager::CreateForWebContents(
1709       main_web_contents_);
1710   web_modal::WebContentsModalDialogManager::FromWebContents(main_web_contents_)
1711       ->SetDelegate(browser);
1712 }
1713 
OnReattachMainTargetComplete(base::Value)1714 void DevToolsWindow::OnReattachMainTargetComplete(base::Value) {
1715   std::move(reattach_complete_callback_).Run();
1716 }
1717 
OnLocaleChanged()1718 void DevToolsWindow::OnLocaleChanged() {
1719   OverrideAndSyncDevToolsRendererPrefs();
1720 }
1721 
OverrideAndSyncDevToolsRendererPrefs()1722 void DevToolsWindow::OverrideAndSyncDevToolsRendererPrefs() {
1723   main_web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false;
1724   main_web_contents_->GetMutableRendererPrefs()->accept_languages =
1725       g_browser_process->GetApplicationLocale();
1726   main_web_contents_->SyncRendererPrefs();
1727 }
1728