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