• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 #include "base/command_line.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/lazy_instance.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/extension_system.h"
23 #include "chrome/browser/extensions/extension_web_contents_observer.h"
24 #include "chrome/browser/file_select_helper.h"
25 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
26 #include "chrome/browser/infobars/infobar.h"
27 #include "chrome/browser/prefs/pref_service_syncable.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/sessions/session_tab_helper.h"
30 #include "chrome/browser/themes/theme_properties.h"
31 #include "chrome/browser/themes/theme_service.h"
32 #include "chrome/browser/themes/theme_service_factory.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_dialogs.h"
35 #include "chrome/browser/ui/browser_iterator.h"
36 #include "chrome/browser/ui/browser_list.h"
37 #include "chrome/browser/ui/browser_window.h"
38 #include "chrome/browser/ui/host_desktop.h"
39 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
40 #include "chrome/browser/ui/tabs/tab_strip_model.h"
41 #include "chrome/browser/ui/webui/devtools_ui.h"
42 #include "chrome/common/chrome_switches.h"
43 #include "chrome/common/extensions/manifest_url_handler.h"
44 #include "chrome/common/pref_names.h"
45 #include "chrome/common/render_messages.h"
46 #include "chrome/common/url_constants.h"
47 #include "components/user_prefs/pref_registry_syncable.h"
48 #include "content/public/browser/browser_thread.h"
49 #include "content/public/browser/child_process_security_policy.h"
50 #include "content/public/browser/devtools_agent_host.h"
51 #include "content/public/browser/devtools_client_host.h"
52 #include "content/public/browser/devtools_manager.h"
53 #include "content/public/browser/favicon_status.h"
54 #include "content/public/browser/load_notification_details.h"
55 #include "content/public/browser/navigation_controller.h"
56 #include "content/public/browser/navigation_entry.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/render_process_host.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/user_metrics.h"
61 #include "content/public/browser/web_contents.h"
62 #include "content/public/browser/web_contents_observer.h"
63 #include "content/public/browser/web_contents_view.h"
64 #include "content/public/common/bindings_policy.h"
65 #include "content/public/common/content_client.h"
66 #include "content/public/common/page_transition_types.h"
67 #include "content/public/common/url_constants.h"
68 #include "grit/generated_resources.h"
69 #include "ui/base/l10n/l10n_util.h"
70 
71 using base::DictionaryValue;
72 using content::BrowserThread;
73 using content::DevToolsAgentHost;
74 
75 
76 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
77 
78 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
79  public:
80   // If |infobar_service| is NULL, runs |callback| with a single argument with
81   // value "false".  Otherwise, creates a dev tools confirm infobar and delegate
82   // and adds the inofbar to |infobar_service|.
83   static void Create(InfoBarService* infobar_service,
84                      const DevToolsWindow::InfoBarCallback& callback,
85                      const base::string16& message);
86 
87  private:
88   DevToolsConfirmInfoBarDelegate(
89       const DevToolsWindow::InfoBarCallback& callback,
90       const base::string16& message);
91   virtual ~DevToolsConfirmInfoBarDelegate();
92 
93   virtual base::string16 GetMessageText() const OVERRIDE;
94   virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
95   virtual bool Accept() OVERRIDE;
96   virtual bool Cancel() OVERRIDE;
97 
98   DevToolsWindow::InfoBarCallback callback_;
99   const base::string16 message_;
100 
101   DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
102 };
103 
Create(InfoBarService * infobar_service,const DevToolsWindow::InfoBarCallback & callback,const base::string16 & message)104 void DevToolsConfirmInfoBarDelegate::Create(
105     InfoBarService* infobar_service,
106     const DevToolsWindow::InfoBarCallback& callback,
107     const base::string16& message) {
108   if (!infobar_service) {
109     callback.Run(false);
110     return;
111   }
112 
113   infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
114       scoped_ptr<ConfirmInfoBarDelegate>(
115           new DevToolsConfirmInfoBarDelegate(callback, message))));
116 }
117 
DevToolsConfirmInfoBarDelegate(const DevToolsWindow::InfoBarCallback & callback,const base::string16 & message)118 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
119     const DevToolsWindow::InfoBarCallback& callback,
120     const base::string16& message)
121     : ConfirmInfoBarDelegate(),
122       callback_(callback),
123       message_(message) {
124 }
125 
~DevToolsConfirmInfoBarDelegate()126 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
127   if (!callback_.is_null())
128     callback_.Run(false);
129 }
130 
GetMessageText() const131 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
132   return message_;
133 }
134 
GetButtonLabel(InfoBarButton button) const135 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
136     InfoBarButton button) const {
137   return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
138       IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
139 }
140 
Accept()141 bool DevToolsConfirmInfoBarDelegate::Accept() {
142   callback_.Run(true);
143   callback_.Reset();
144   return true;
145 }
146 
Cancel()147 bool DevToolsConfirmInfoBarDelegate::Cancel() {
148   callback_.Run(false);
149   callback_.Reset();
150   return true;
151 }
152 
153 
154 // DevToolsWindow::InspectedWebContentsObserver -------------------------------
155 
156 class DevToolsWindow::InspectedWebContentsObserver
157     : public content::WebContentsObserver {
158  public:
159   explicit InspectedWebContentsObserver(content::WebContents* web_contents);
160   virtual ~InspectedWebContentsObserver();
161 
web_contents()162   content::WebContents* web_contents() {
163     return WebContentsObserver::web_contents();
164   }
165 
166  private:
167   DISALLOW_COPY_AND_ASSIGN(InspectedWebContentsObserver);
168 };
169 
InspectedWebContentsObserver(content::WebContents * web_contents)170 DevToolsWindow::InspectedWebContentsObserver::InspectedWebContentsObserver(
171     content::WebContents* web_contents)
172     : WebContentsObserver(web_contents) {
173 }
174 
~InspectedWebContentsObserver()175 DevToolsWindow::InspectedWebContentsObserver::~InspectedWebContentsObserver() {
176 }
177 
178 
179 // DevToolsWindow::FrontendWebContentsObserver --------------------------------
180 
181 class DevToolsWindow::FrontendWebContentsObserver
182     : public content::WebContentsObserver {
183  public:
184   explicit FrontendWebContentsObserver(DevToolsWindow* window);
185   virtual ~FrontendWebContentsObserver();
186 
187  private:
188   // contents::WebContentsObserver:
189   virtual void AboutToNavigateRenderView(
190       content::RenderViewHost* render_view_host) OVERRIDE;
191   virtual void DocumentOnLoadCompletedInMainFrame(int32 page_id) OVERRIDE;
192   virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE;
193 
194   DevToolsWindow* devtools_window_;
195   DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
196 };
197 
FrontendWebContentsObserver(DevToolsWindow * devtools_window)198 DevToolsWindow::FrontendWebContentsObserver::FrontendWebContentsObserver(
199     DevToolsWindow* devtools_window)
200     : WebContentsObserver(devtools_window->web_contents()),
201       devtools_window_(devtools_window) {
202 }
203 
WebContentsDestroyed(content::WebContents * contents)204 void DevToolsWindow::FrontendWebContentsObserver::WebContentsDestroyed(
205     content::WebContents* contents) {
206   delete devtools_window_;
207 }
208 
~FrontendWebContentsObserver()209 DevToolsWindow::FrontendWebContentsObserver::~FrontendWebContentsObserver() {
210 }
211 
AboutToNavigateRenderView(content::RenderViewHost * render_view_host)212 void DevToolsWindow::FrontendWebContentsObserver::AboutToNavigateRenderView(
213     content::RenderViewHost* render_view_host) {
214   content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host);
215 }
216 
217 void DevToolsWindow::FrontendWebContentsObserver::
DocumentOnLoadCompletedInMainFrame(int32 page_id)218     DocumentOnLoadCompletedInMainFrame(int32 page_id) {
219   devtools_window_->DocumentOnLoadCompletedInMainFrame();
220 }
221 
222 // DevToolsWindow -------------------------------------------------------------
223 
224 namespace {
225 
226 typedef std::vector<DevToolsWindow*> DevToolsWindows;
227 base::LazyInstance<DevToolsWindows>::Leaky g_instances =
228     LAZY_INSTANCE_INITIALIZER;
229 
230 const char kPrefBottom[] = "dock_bottom";
231 const char kPrefRight[] = "dock_right";
232 const char kPrefUndocked[] = "undocked";
233 
234 const char kDockSideBottom[] = "bottom";
235 const char kDockSideRight[] = "right";
236 const char kDockSideUndocked[] = "undocked";
237 const char kDockSideMinimized[] = "minimized";
238 
239 static const char kFrontendHostId[] = "id";
240 static const char kFrontendHostMethod[] = "method";
241 static const char kFrontendHostParams[] = "params";
242 
243 const int kMinContentsSize = 50;
244 
SkColorToRGBAString(SkColor color)245 std::string SkColorToRGBAString(SkColor color) {
246   // We avoid StringPrintf because it will use locale specific formatters for
247   // the double (e.g. ',' instead of '.' in German).
248   return "rgba(" + base::IntToString(SkColorGetR(color)) + "," +
249       base::IntToString(SkColorGetG(color)) + "," +
250       base::IntToString(SkColorGetB(color)) + "," +
251       base::DoubleToString(SkColorGetA(color) / 255.0) + ")";
252 }
253 
CreateFileSystemValue(DevToolsFileHelper::FileSystem file_system)254 DictionaryValue* CreateFileSystemValue(
255     DevToolsFileHelper::FileSystem file_system) {
256   DictionaryValue* file_system_value = new DictionaryValue();
257   file_system_value->SetString("fileSystemName", file_system.file_system_name);
258   file_system_value->SetString("rootURL", file_system.root_url);
259   file_system_value->SetString("fileSystemPath", file_system.file_system_path);
260   return file_system_value;
261 }
262 
263 }  // namespace
264 
265 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
266 
~DevToolsWindow()267 DevToolsWindow::~DevToolsWindow() {
268   content::DevToolsManager::GetInstance()->ClientHostClosing(
269       frontend_host_.get());
270   UpdateBrowserToolbar();
271 
272   DevToolsWindows* instances = &g_instances.Get();
273   DevToolsWindows::iterator it(
274       std::find(instances->begin(), instances->end(), this));
275   DCHECK(it != instances->end());
276   instances->erase(it);
277 
278   for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
279        jobs_it != indexing_jobs_.end(); ++jobs_it) {
280     jobs_it->second->Stop();
281   }
282   indexing_jobs_.clear();
283 }
284 
285 // static
GetDevToolsWindowPlacementPrefKey()286 std::string DevToolsWindow::GetDevToolsWindowPlacementPrefKey() {
287   return std::string(prefs::kBrowserWindowPlacement) + "_" +
288       std::string(kDevToolsApp);
289 }
290 
291 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)292 void DevToolsWindow::RegisterProfilePrefs(
293     user_prefs::PrefRegistrySyncable* registry) {
294   registry->RegisterBooleanPref(
295       prefs::kDevToolsOpenDocked, true,
296       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
297   registry->RegisterStringPref(
298       prefs::kDevToolsDockSide, kDockSideBottom,
299       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
300   registry->RegisterDictionaryPref(
301       prefs::kDevToolsEditedFiles,
302       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
303   registry->RegisterDictionaryPref(
304       prefs::kDevToolsFileSystemPaths,
305       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
306   registry->RegisterStringPref(
307       prefs::kDevToolsAdbKey, std::string(),
308       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
309 
310   registry->RegisterDictionaryPref(
311       GetDevToolsWindowPlacementPrefKey().c_str(),
312       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
313 
314   registry->RegisterBooleanPref(
315       prefs::kDevToolsDiscoverUsbDevicesEnabled,
316       false,
317       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
318   registry->RegisterBooleanPref(
319       prefs::kDevToolsPortForwardingEnabled,
320       false,
321       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
322   registry->RegisterBooleanPref(
323       prefs::kDevToolsPortForwardingDefaultSet,
324       false,
325       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
326   registry->RegisterDictionaryPref(
327       prefs::kDevToolsPortForwardingConfig,
328       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
329 }
330 
331 // static
GetDockedInstanceForInspectedTab(content::WebContents * inspected_web_contents)332 DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab(
333     content::WebContents* inspected_web_contents) {
334   DevToolsWindow* window = GetInstanceForInspectedRenderViewHost(
335       inspected_web_contents->GetRenderViewHost());
336   return (window && window->IsDocked()) ? window : NULL;
337 }
338 
339 // static
GetInstanceForInspectedRenderViewHost(content::RenderViewHost * inspected_rvh)340 DevToolsWindow* DevToolsWindow::GetInstanceForInspectedRenderViewHost(
341       content::RenderViewHost* inspected_rvh) {
342   if (!inspected_rvh || !DevToolsAgentHost::HasFor(inspected_rvh))
343     return NULL;
344 
345   scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetOrCreateFor(
346       inspected_rvh));
347   return FindDevToolsWindow(agent.get());
348 }
349 
350 // static
IsDevToolsWindow(content::RenderViewHost * window_rvh)351 bool DevToolsWindow::IsDevToolsWindow(content::RenderViewHost* window_rvh) {
352   return AsDevToolsWindow(window_rvh) != NULL;
353 }
354 
355 // static
OpenDevToolsWindowForWorker(Profile * profile,DevToolsAgentHost * worker_agent)356 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker(
357     Profile* profile,
358     DevToolsAgentHost* worker_agent) {
359   DevToolsWindow* window = FindDevToolsWindow(worker_agent);
360   if (!window) {
361     window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
362     // Will disconnect the current client host if there is one.
363     content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
364         worker_agent, window->frontend_host_.get());
365   }
366   window->Show(DevToolsToggleAction::Show());
367   return window;
368 }
369 
370 // static
CreateDevToolsWindowForWorker(Profile * profile)371 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
372     Profile* profile) {
373   content::RecordAction(content::UserMetricsAction("DevTools_InspectWorker"));
374   return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true,
375                 false, false);
376 }
377 
378 // static
OpenDevToolsWindow(content::RenderViewHost * inspected_rvh)379 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow(
380     content::RenderViewHost* inspected_rvh) {
381   return ToggleDevToolsWindow(
382       inspected_rvh, true, DevToolsToggleAction::Show());
383 }
384 
385 // static
ToggleDevToolsWindow(Browser * browser,const DevToolsToggleAction & action)386 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
387     Browser* browser,
388     const DevToolsToggleAction& action) {
389   if (action.type() == DevToolsToggleAction::kToggle &&
390       browser->is_devtools()) {
391     browser->tab_strip_model()->CloseAllTabs();
392     return NULL;
393   }
394 
395   return ToggleDevToolsWindow(
396       browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
397       action.type() == DevToolsToggleAction::kInspect, action);
398 }
399 
400 // static
OpenExternalFrontend(Profile * profile,const std::string & frontend_url,content::DevToolsAgentHost * agent_host)401 void DevToolsWindow::OpenExternalFrontend(
402     Profile* profile,
403     const std::string& frontend_url,
404     content::DevToolsAgentHost* agent_host) {
405   DevToolsWindow* window = FindDevToolsWindow(agent_host);
406   if (!window) {
407     window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
408                     DEVTOOLS_DOCK_SIDE_UNDOCKED, false, true, false);
409     content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
410         agent_host, window->frontend_host_.get());
411   }
412   window->Show(DevToolsToggleAction::Show());
413 }
414 
415 // static
ToggleDevToolsWindow(content::RenderViewHost * inspected_rvh,bool force_open,const DevToolsToggleAction & action)416 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow(
417     content::RenderViewHost* inspected_rvh,
418     bool force_open,
419     const DevToolsToggleAction& action) {
420   scoped_refptr<DevToolsAgentHost> agent(
421       DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
422   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
423   DevToolsWindow* window = FindDevToolsWindow(agent.get());
424   bool do_open = force_open;
425   if (!window) {
426     Profile* profile = Profile::FromBrowserContext(
427         inspected_rvh->GetProcess()->GetBrowserContext());
428     DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
429     content::RecordAction(
430         content::UserMetricsAction("DevTools_InspectRenderer"));
431     window = Create(profile, GURL(), inspected_rvh, dock_side, false, false,
432         true);
433     manager->RegisterDevToolsClientHostFor(agent.get(),
434                                            window->frontend_host_.get());
435     do_open = true;
436   }
437 
438   // Update toolbar to reflect DevTools changes.
439   window->UpdateBrowserToolbar();
440 
441   // If window is docked and visible, we hide it on toggle. If window is
442   // undocked, we show (activate) it. If window is minimized, we maximize it.
443   if (window->dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
444     window->Restore();
445   else if (!window->IsDocked() || do_open)
446     window->Show(action);
447   else
448     window->CloseWindow();
449 
450   return window;
451 }
452 
453 // static
InspectElement(content::RenderViewHost * inspected_rvh,int x,int y)454 void DevToolsWindow::InspectElement(content::RenderViewHost* inspected_rvh,
455                                     int x,
456                                     int y) {
457   scoped_refptr<DevToolsAgentHost> agent(
458       DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
459   agent->InspectElement(x, y);
460   // TODO(loislo): we should initiate DevTools window opening from within
461   // renderer. Otherwise, we still can hit a race condition here.
462   OpenDevToolsWindow(inspected_rvh);
463 }
464 
465 // static
GetMinimumWidth()466 int DevToolsWindow::GetMinimumWidth() {
467   const int kMinDevToolsWidth = 150;
468   return kMinDevToolsWidth;
469 }
470 
471 // static
GetMinimumHeight()472 int DevToolsWindow::GetMinimumHeight() {
473   // Minimal height of devtools pane or content pane when devtools are docked
474   // to the browser window.
475   const int kMinDevToolsHeight = 50;
476   return kMinDevToolsHeight;
477 }
478 
479 // static
GetMinimizedHeight()480 int DevToolsWindow::GetMinimizedHeight() {
481   const int kMinimizedDevToolsHeight = 24;
482   return kMinimizedDevToolsHeight;
483 }
484 
InspectedContentsClosing()485 void DevToolsWindow::InspectedContentsClosing() {
486   intercepted_page_beforeunload_ = false;
487   web_contents_->GetRenderViewHost()->ClosePage();
488 }
489 
GetRenderViewHost()490 content::RenderViewHost* DevToolsWindow::GetRenderViewHost() {
491   return web_contents_->GetRenderViewHost();
492 }
493 
GetDevToolsClientHostForTest()494 content::DevToolsClientHost* DevToolsWindow::GetDevToolsClientHostForTest() {
495   return frontend_host_.get();
496 }
497 
GetWidth(int container_width)498 int DevToolsWindow::GetWidth(int container_width) {
499   if (width_ == -1) {
500     width_ = profile_->GetPrefs()->
501         GetInteger(prefs::kDevToolsVSplitLocation);
502   }
503 
504   // By default, size devtools as 1/3 of the browser window.
505   if (width_ == -1)
506     width_ = container_width / 3;
507 
508   // Respect the minimum devtools width preset.
509   width_ = std::max(GetMinimumWidth(), width_);
510 
511   // But it should never compromise the content window size unless the entire
512   // window is tiny.
513   width_ = std::min(container_width - kMinContentsSize, width_);
514   return width_;
515 }
516 
GetHeight(int container_height)517 int DevToolsWindow::GetHeight(int container_height) {
518   if (height_ == -1) {
519     height_ = profile_->GetPrefs()->
520         GetInteger(prefs::kDevToolsHSplitLocation);
521   }
522 
523   // By default, size devtools as 1/3 of the browser window.
524   if (height_ == -1)
525     height_ = container_height / 3;
526 
527   // Respect the minimum devtools width preset.
528   height_ = std::max(GetMinimumHeight(), height_);
529 
530   // But it should never compromise the content window size.
531   height_ = std::min(container_height - kMinContentsSize, height_);
532   return height_;
533 }
534 
SetWidth(int width)535 void DevToolsWindow::SetWidth(int width) {
536   width_ = width;
537   profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width);
538 }
539 
SetHeight(int height)540 void DevToolsWindow::SetHeight(int height) {
541   height_ = height;
542   profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height);
543 }
544 
Show(const DevToolsToggleAction & action)545 void DevToolsWindow::Show(const DevToolsToggleAction& action) {
546   if (IsDocked()) {
547     Browser* inspected_browser = NULL;
548     int inspected_tab_index = -1;
549     // Tell inspected browser to update splitter and switch to inspected panel.
550     if (!IsInspectedBrowserPopup() &&
551         FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
552                                         &inspected_browser,
553                                         &inspected_tab_index)) {
554       BrowserWindow* inspected_window = inspected_browser->window();
555       web_contents_->SetDelegate(this);
556       inspected_window->UpdateDevTools();
557       web_contents_->GetView()->SetInitialFocus();
558       inspected_window->Show();
559       TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
560       tab_strip_model->ActivateTabAt(inspected_tab_index, true);
561       PrefsTabHelper::CreateForWebContents(web_contents_);
562       GetRenderViewHost()->SyncRendererPrefs();
563       ScheduleAction(action);
564       return;
565     }
566 
567     // Sometimes we don't know where to dock. Stay undocked.
568     dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED;
569   }
570 
571   // Avoid consecutive window switching if the devtools window has been opened
572   // and the Inspect Element shortcut is pressed in the inspected tab.
573   bool should_show_window =
574       !browser_ || (action.type() != DevToolsToggleAction::kInspect);
575 
576   if (!browser_)
577     CreateDevToolsBrowser();
578 
579   if (should_show_window) {
580     browser_->window()->Show();
581     web_contents_->GetView()->SetInitialFocus();
582   }
583 
584   ScheduleAction(action);
585 }
586 
587 // static
HandleBeforeUnload(content::WebContents * frontend_contents,bool proceed,bool * proceed_to_fire_unload)588 bool DevToolsWindow::HandleBeforeUnload(content::WebContents* frontend_contents,
589     bool proceed, bool* proceed_to_fire_unload) {
590   DevToolsWindow* window = AsDevToolsWindow(
591       frontend_contents->GetRenderViewHost());
592   if (!window)
593     return false;
594   if (!window->intercepted_page_beforeunload_)
595     return false;
596   window->BeforeUnloadFired(frontend_contents, proceed,
597       proceed_to_fire_unload);
598   return true;
599 }
600 
601 // static
InterceptPageBeforeUnload(content::WebContents * contents)602 bool DevToolsWindow::InterceptPageBeforeUnload(content::WebContents* contents) {
603   DevToolsWindow* window =
604       DevToolsWindow::GetInstanceForInspectedRenderViewHost(
605           contents->GetRenderViewHost());
606   if (!window || window->intercepted_page_beforeunload_)
607     return false;
608 
609   window->intercepted_page_beforeunload_ = true;
610   // Handle case of devtools inspecting another devtools instance by passing
611   // the call up to the inspecting devtools instance.
612   if (!DevToolsWindow::InterceptPageBeforeUnload(window->web_contents())) {
613     window->web_contents()->GetRenderViewHost()->FirePageBeforeUnload(false);
614   }
615   return true;
616 }
617 
618 // static
NeedsToInterceptBeforeUnload(content::WebContents * contents)619 bool DevToolsWindow::NeedsToInterceptBeforeUnload(
620     content::WebContents* contents) {
621   DevToolsWindow* window =
622       DevToolsWindow::GetInstanceForInspectedRenderViewHost(
623           contents->GetRenderViewHost());
624   return window && !window->intercepted_page_beforeunload_;
625 }
626 
627 // static
HasFiredBeforeUnloadEventForDevToolsBrowser(Browser * browser)628 bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
629     Browser* browser) {
630   DCHECK(browser->is_devtools());
631   // When FastUnloadController is used, devtools frontend will be detached
632   // from the browser window at this point which means we've already fired
633   // beforeunload.
634   if (browser->tab_strip_model()->empty())
635     return true;
636   content::WebContents* contents =
637       browser->tab_strip_model()->GetWebContentsAt(0);
638   DevToolsWindow* window = AsDevToolsWindow(contents->GetRenderViewHost());
639   if (!window)
640     return false;
641   return window->intercepted_page_beforeunload_;
642 }
643 
644 // static
OnPageCloseCanceled(content::WebContents * contents)645 void DevToolsWindow::OnPageCloseCanceled(content::WebContents* contents) {
646   DevToolsWindow *window =
647       DevToolsWindow::GetInstanceForInspectedRenderViewHost(
648           contents->GetRenderViewHost());
649   if (!window)
650     return;
651   window->intercepted_page_beforeunload_ = false;
652   // Propagate to devtools opened on devtools if any.
653   DevToolsWindow::OnPageCloseCanceled(window->web_contents());
654 }
655 
SetDockSideForTest(DevToolsDockSide dock_side)656 void DevToolsWindow::SetDockSideForTest(DevToolsDockSide dock_side) {
657   SetDockSide(SideToString(dock_side));
658 }
659 
DevToolsWindow(Profile * profile,const GURL & url,content::RenderViewHost * inspected_rvh,DevToolsDockSide dock_side)660 DevToolsWindow::DevToolsWindow(Profile* profile,
661                                const GURL& url,
662                                content::RenderViewHost* inspected_rvh,
663                                DevToolsDockSide dock_side)
664     : profile_(profile),
665       browser_(NULL),
666       dock_side_(dock_side),
667       is_loaded_(false),
668       action_on_load_(DevToolsToggleAction::Show()),
669       width_(-1),
670       height_(-1),
671       dock_side_before_minimized_(dock_side),
672       intercepted_page_beforeunload_(false),
673       weak_factory_(this) {
674   web_contents_ =
675       content::WebContents::Create(content::WebContents::CreateParams(profile));
676   frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
677 
678   web_contents_->GetController().LoadURL(url, content::Referrer(),
679       content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
680 
681   frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost(
682       web_contents_, this));
683   file_helper_.reset(new DevToolsFileHelper(web_contents_, profile));
684   file_system_indexer_ = new DevToolsFileSystemIndexer();
685   extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents_);
686 
687   g_instances.Get().push_back(this);
688 
689   // Wipe out page icon so that the default application icon is used.
690   content::NavigationEntry* entry =
691       web_contents_->GetController().GetActiveEntry();
692   entry->GetFavicon().image = gfx::Image();
693   entry->GetFavicon().valid = true;
694 
695   // Register on-load actions.
696   registrar_.Add(
697       this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
698       content::Source<ThemeService>(
699           ThemeServiceFactory::GetForProfile(profile_)));
700 
701   // There is no inspected_rvh in case of shared workers.
702   if (inspected_rvh)
703     inspected_contents_observer_.reset(new InspectedWebContentsObserver(
704         content::WebContents::FromRenderViewHost(inspected_rvh)));
705 
706   embedder_message_dispatcher_.reset(
707       new DevToolsEmbedderMessageDispatcher(this));
708 }
709 
710 // static
Create(Profile * profile,const GURL & frontend_url,content::RenderViewHost * inspected_rvh,DevToolsDockSide dock_side,bool shared_worker_frontend,bool external_frontend,bool can_dock)711 DevToolsWindow* DevToolsWindow::Create(
712     Profile* profile,
713     const GURL& frontend_url,
714     content::RenderViewHost* inspected_rvh,
715     DevToolsDockSide dock_side,
716     bool shared_worker_frontend,
717     bool external_frontend,
718     bool can_dock) {
719   if (inspected_rvh) {
720     // Check for a place to dock.
721     Browser* browser = NULL;
722     int tab;
723     content::WebContents* inspected_web_contents =
724         content::WebContents::FromRenderViewHost(inspected_rvh);
725     if (!FindInspectedBrowserAndTabIndex(inspected_web_contents,
726                                          &browser, &tab) ||
727         browser->is_type_popup()) {
728       can_dock = false;
729     }
730   }
731 
732   // Create WebContents with devtools.
733   GURL url(GetDevToolsURL(profile, frontend_url, dock_side,
734                           shared_worker_frontend,
735                           external_frontend,
736                           can_dock));
737   return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
738 }
739 
740 // static
GetDevToolsURL(Profile * profile,const GURL & base_url,DevToolsDockSide dock_side,bool shared_worker_frontend,bool external_frontend,bool can_dock)741 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
742                                     const GURL& base_url,
743                                     DevToolsDockSide dock_side,
744                                     bool shared_worker_frontend,
745                                     bool external_frontend,
746                                     bool can_dock) {
747   if (base_url.SchemeIs("data"))
748     return base_url;
749 
750   std::string frontend_url(
751       base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
752   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
753   DCHECK(tp);
754   std::string url_string(
755       frontend_url +
756       ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
757       "dockSide=" + SideToString(dock_side) +
758       "&toolbarColor=" +
759       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
760       "&textColor=" +
761       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
762   if (shared_worker_frontend)
763     url_string += "&isSharedWorker=true";
764   if (external_frontend)
765     url_string += "&remoteFrontend=true";
766   if (can_dock)
767     url_string += "&can_dock=true";
768   if (CommandLine::ForCurrentProcess()->HasSwitch(
769       switches::kEnableDevToolsExperiments))
770     url_string += "&experiments=true";
771   url_string += "&updateAppcache";
772   return GURL(url_string);
773 }
774 
775 // static
FindDevToolsWindow(DevToolsAgentHost * agent_host)776 DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
777     DevToolsAgentHost* agent_host) {
778   DevToolsWindows* instances = &g_instances.Get();
779   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
780   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
781        ++it) {
782     if (manager->GetDevToolsAgentHostFor((*it)->frontend_host_.get()) ==
783         agent_host)
784       return *it;
785   }
786   return NULL;
787 }
788 
789 // static
AsDevToolsWindow(content::RenderViewHost * window_rvh)790 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
791     content::RenderViewHost* window_rvh) {
792   if (g_instances == NULL)
793     return NULL;
794   DevToolsWindows* instances = &g_instances.Get();
795   for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
796        ++it) {
797     if ((*it)->web_contents_->GetRenderViewHost() == window_rvh)
798       return *it;
799   }
800   return NULL;
801 }
802 
803 // static
GetDockSideFromPrefs(Profile * profile)804 DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) {
805   std::string dock_side =
806       profile->GetPrefs()->GetString(prefs::kDevToolsDockSide);
807 
808   // Migrate prefs.
809   const char kOldPrefBottom[] = "bottom";
810   const char kOldPrefRight[] = "right";
811   if ((dock_side == kOldPrefBottom) || (dock_side == kOldPrefRight)) {
812     if (!profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked))
813       return DEVTOOLS_DOCK_SIDE_UNDOCKED;
814     return (dock_side == kOldPrefBottom) ?
815         DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_RIGHT;
816   }
817 
818   if (dock_side == kPrefUndocked)
819     return DEVTOOLS_DOCK_SIDE_UNDOCKED;
820   if (dock_side == kPrefRight)
821     return DEVTOOLS_DOCK_SIDE_RIGHT;
822   // Default to docked to bottom.
823   return DEVTOOLS_DOCK_SIDE_BOTTOM;
824 }
825 
826 // static
SideToString(DevToolsDockSide dock_side)827 std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) {
828   switch (dock_side) {
829     case DEVTOOLS_DOCK_SIDE_UNDOCKED:  return kDockSideUndocked;
830     case DEVTOOLS_DOCK_SIDE_RIGHT:     return kDockSideRight;
831     case DEVTOOLS_DOCK_SIDE_BOTTOM:    return kDockSideBottom;
832     case DEVTOOLS_DOCK_SIDE_MINIMIZED: return kDockSideMinimized;
833     default:                           return kDockSideUndocked;
834   }
835 }
836 
837 // static
SideFromString(const std::string & dock_side)838 DevToolsDockSide DevToolsWindow::SideFromString(
839     const std::string& dock_side) {
840   if (dock_side == kDockSideRight)
841     return DEVTOOLS_DOCK_SIDE_RIGHT;
842   if (dock_side == kDockSideBottom)
843     return DEVTOOLS_DOCK_SIDE_BOTTOM;
844   return (dock_side == kDockSideMinimized) ?
845       DEVTOOLS_DOCK_SIDE_MINIMIZED : DEVTOOLS_DOCK_SIDE_UNDOCKED;
846 }
847 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)848 void DevToolsWindow::Observe(int type,
849                              const content::NotificationSource& source,
850                              const content::NotificationDetails& details) {
851   DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
852   UpdateTheme();
853 }
854 
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)855 content::WebContents* DevToolsWindow::OpenURLFromTab(
856     content::WebContents* source,
857     const content::OpenURLParams& params) {
858   if (!params.url.SchemeIs(chrome::kChromeDevToolsScheme)) {
859     content::WebContents* inspected_web_contents = GetInspectedWebContents();
860     return inspected_web_contents ?
861         inspected_web_contents->OpenURL(params) : NULL;
862   }
863 
864   content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
865   scoped_refptr<DevToolsAgentHost> agent_host(
866       manager->GetDevToolsAgentHostFor(frontend_host_.get()));
867   if (!agent_host.get())
868     return NULL;
869   manager->ClientHostClosing(frontend_host_.get());
870   manager->RegisterDevToolsClientHostFor(agent_host.get(),
871                                          frontend_host_.get());
872 
873   chrome::NavigateParams nav_params(profile_, params.url, params.transition);
874   FillNavigateParamsFromOpenURLParams(&nav_params, params);
875   nav_params.source_contents = source;
876   nav_params.tabstrip_add_types = TabStripModel::ADD_NONE;
877   nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
878   nav_params.user_gesture = params.user_gesture;
879   chrome::Navigate(&nav_params);
880   return nav_params.target_contents;
881 }
882 
AddNewContents(content::WebContents * source,content::WebContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture,bool * was_blocked)883 void DevToolsWindow::AddNewContents(content::WebContents* source,
884                                     content::WebContents* new_contents,
885                                     WindowOpenDisposition disposition,
886                                     const gfx::Rect& initial_pos,
887                                     bool user_gesture,
888                                     bool* was_blocked) {
889   content::WebContents* inspected_web_contents = GetInspectedWebContents();
890   if (inspected_web_contents) {
891     inspected_web_contents->GetDelegate()->AddNewContents(
892         source, new_contents, disposition, initial_pos, user_gesture,
893         was_blocked);
894   }
895 }
896 
CloseContents(content::WebContents * source)897 void DevToolsWindow::CloseContents(content::WebContents* source) {
898   CHECK(IsDocked());
899   // Update dev tools to reflect removed dev tools window.
900   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
901   if (inspected_window)
902     inspected_window->UpdateDevTools();
903   // In case of docked web_contents_, we own it so delete here.
904   // Embedding DevTools window will be deleted as a result of
905   // WebContentsDestroyed callback.
906   delete web_contents_;
907 }
908 
BeforeUnloadFired(content::WebContents * tab,bool proceed,bool * proceed_to_fire_unload)909 void DevToolsWindow::BeforeUnloadFired(content::WebContents* tab,
910                                        bool proceed,
911                                        bool* proceed_to_fire_unload) {
912   if (!intercepted_page_beforeunload_) {
913     // Docked devtools window closed directly.
914     if (proceed) {
915       content::DevToolsManager::GetInstance()->ClientHostClosing(
916           frontend_host_.get());
917     }
918     *proceed_to_fire_unload = proceed;
919   } else {
920     // Inspected page is attempting to close.
921     content::WebContents* inspected_web_contents = GetInspectedWebContents();
922     if (proceed) {
923       inspected_web_contents->GetRenderViewHost()->FirePageBeforeUnload(false);
924     } else {
925       bool should_proceed;
926       inspected_web_contents->GetDelegate()->BeforeUnloadFired(
927           inspected_web_contents, false, &should_proceed);
928       DCHECK(!should_proceed);
929     }
930     *proceed_to_fire_unload = false;
931   }
932 }
933 
PreHandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)934 bool DevToolsWindow::PreHandleKeyboardEvent(
935     content::WebContents* source,
936     const content::NativeWebKeyboardEvent& event,
937     bool* is_keyboard_shortcut) {
938   if (IsDocked()) {
939     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
940     if (inspected_window) {
941       return inspected_window->PreHandleKeyboardEvent(event,
942                                                       is_keyboard_shortcut);
943     }
944   }
945   return false;
946 }
947 
HandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)948 void DevToolsWindow::HandleKeyboardEvent(
949     content::WebContents* source,
950     const content::NativeWebKeyboardEvent& event) {
951   if (IsDocked()) {
952     if (event.windowsKeyCode == 0x08) {
953       // Do not navigate back in history on Windows (http://crbug.com/74156).
954       return;
955     }
956     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
957     if (inspected_window)
958       inspected_window->HandleKeyboardEvent(event);
959   }
960 }
961 
GetJavaScriptDialogManager()962 content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager() {
963   content::WebContents* inspected_web_contents = GetInspectedWebContents();
964   return (inspected_web_contents && inspected_web_contents->GetDelegate()) ?
965       inspected_web_contents->GetDelegate()->GetJavaScriptDialogManager() :
966       content::WebContentsDelegate::GetJavaScriptDialogManager();
967 }
968 
OpenColorChooser(content::WebContents * web_contents,SkColor initial_color,const std::vector<content::ColorSuggestion> & suggestions)969 content::ColorChooser* DevToolsWindow::OpenColorChooser(
970     content::WebContents* web_contents,
971     SkColor initial_color,
972     const std::vector<content::ColorSuggestion>& suggestions) {
973   return chrome::ShowColorChooser(web_contents, initial_color);
974 }
975 
RunFileChooser(content::WebContents * web_contents,const content::FileChooserParams & params)976 void DevToolsWindow::RunFileChooser(content::WebContents* web_contents,
977                                     const content::FileChooserParams& params) {
978   FileSelectHelper::RunFileChooser(web_contents, params);
979 }
980 
WebContentsFocused(content::WebContents * contents)981 void DevToolsWindow::WebContentsFocused(content::WebContents* contents) {
982   Browser* inspected_browser = NULL;
983   int inspected_tab_index = -1;
984   if (IsDocked() && FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
985                                                     &inspected_browser,
986                                                     &inspected_tab_index))
987     inspected_browser->window()->WebContentsFocused(contents);
988 }
989 
DispatchOnEmbedder(const std::string & message)990 void DevToolsWindow::DispatchOnEmbedder(const std::string& message) {
991   std::string method;
992   base::ListValue empty_params;
993   base::ListValue* params = &empty_params;
994 
995   base::DictionaryValue* dict = NULL;
996   scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
997   if (!parsed_message ||
998       !parsed_message->GetAsDictionary(&dict) ||
999       !dict->GetString(kFrontendHostMethod, &method) ||
1000       (dict->HasKey(kFrontendHostParams) &&
1001           !dict->GetList(kFrontendHostParams, &params))) {
1002     LOG(ERROR) << "Invalid message was sent to embedder: " << message;
1003     return;
1004   }
1005 
1006   int id = 0;
1007   dict->GetInteger(kFrontendHostId, &id);
1008 
1009   std::string error = embedder_message_dispatcher_->Dispatch(method, params);
1010   if (id) {
1011     scoped_ptr<base::Value> id_value(base::Value::CreateIntegerValue(id));
1012     scoped_ptr<base::Value> error_value(base::Value::CreateStringValue(error));
1013     CallClientFunction("InspectorFrontendAPI.embedderMessageAck",
1014                        id_value.get(), error_value.get(), NULL);
1015   }
1016 }
1017 
ActivateWindow()1018 void DevToolsWindow::ActivateWindow() {
1019   if (IsDocked() && GetInspectedBrowserWindow())
1020     web_contents_->GetView()->Focus();
1021   else if (!IsDocked() && !browser_->window()->IsActive())
1022     browser_->window()->Activate();
1023 }
1024 
ActivateContents(content::WebContents * contents)1025 void DevToolsWindow::ActivateContents(content::WebContents* contents) {
1026   if (IsDocked()) {
1027     content::WebContents* inspected_tab = this->GetInspectedWebContents();
1028     inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
1029   } else {
1030     browser_->window()->Activate();
1031   }
1032 }
1033 
CloseWindow()1034 void DevToolsWindow::CloseWindow() {
1035   DCHECK(IsDocked());
1036   web_contents_->GetRenderViewHost()->FirePageBeforeUnload(false);
1037 }
1038 
SetWindowBounds(int x,int y,int width,int height)1039 void DevToolsWindow::SetWindowBounds(int x, int y, int width, int height) {
1040   if (!IsDocked())
1041     browser_->window()->SetBounds(gfx::Rect(x, y, width, height));
1042 }
1043 
MoveWindow(int x,int y)1044 void DevToolsWindow::MoveWindow(int x, int y) {
1045   if (!IsDocked()) {
1046     gfx::Rect bounds = browser_->window()->GetBounds();
1047     bounds.Offset(x, y);
1048     browser_->window()->SetBounds(bounds);
1049   }
1050 }
1051 
SetDockSide(const std::string & side)1052 void DevToolsWindow::SetDockSide(const std::string& side) {
1053   DevToolsDockSide requested_side = SideFromString(side);
1054   bool dock_requested = requested_side != DEVTOOLS_DOCK_SIDE_UNDOCKED;
1055   bool is_docked = IsDocked();
1056 
1057   if (dock_requested &&
1058       (!GetInspectedWebContents() || !GetInspectedBrowserWindow() ||
1059        IsInspectedBrowserPopup())) {
1060       // Cannot dock, avoid window flashing due to close-reopen cycle.
1061     return;
1062   }
1063 
1064   if ((dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) &&
1065       (requested_side == DEVTOOLS_DOCK_SIDE_MINIMIZED))
1066     dock_side_before_minimized_ = dock_side_;
1067 
1068   dock_side_ = requested_side;
1069   if (dock_requested && !is_docked) {
1070     // Detach window from the external devtools browser. It will lead to
1071     // the browser object's close and delete. Remove observer first.
1072     TabStripModel* tab_strip_model = browser_->tab_strip_model();
1073     tab_strip_model->DetachWebContentsAt(
1074         tab_strip_model->GetIndexOfWebContents(web_contents_));
1075     browser_ = NULL;
1076   } else if (!dock_requested && is_docked) {
1077     // Update inspected window to hide split and reset it.
1078     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1079     if (inspected_window)
1080       inspected_window->UpdateDevTools();
1081   }
1082 
1083   if (dock_side_ != DEVTOOLS_DOCK_SIDE_MINIMIZED) {
1084     std::string pref_value = kPrefBottom;
1085     switch (dock_side_) {
1086       case DEVTOOLS_DOCK_SIDE_UNDOCKED:
1087           pref_value = kPrefUndocked;
1088           break;
1089       case DEVTOOLS_DOCK_SIDE_RIGHT:
1090           pref_value = kPrefRight;
1091           break;
1092       case DEVTOOLS_DOCK_SIDE_BOTTOM:
1093           pref_value = kPrefBottom;
1094           break;
1095       case DEVTOOLS_DOCK_SIDE_MINIMIZED:
1096           // We don't persist minimized state.
1097           break;
1098     }
1099     profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value);
1100   }
1101 
1102   Show(DevToolsToggleAction::Show());
1103 }
1104 
OpenInNewTab(const std::string & url)1105 void DevToolsWindow::OpenInNewTab(const std::string& url) {
1106   content::OpenURLParams params(
1107       GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
1108       content::PAGE_TRANSITION_LINK, false);
1109   content::WebContents* inspected_web_contents = GetInspectedWebContents();
1110   if (inspected_web_contents) {
1111     inspected_web_contents->OpenURL(params);
1112   } else {
1113     chrome::HostDesktopType host_desktop_type;
1114     if (browser_) {
1115       host_desktop_type = browser_->host_desktop_type();
1116     } else {
1117       // There should always be a browser when there are no inspected web
1118       // contents.
1119       NOTREACHED();
1120       host_desktop_type = chrome::GetActiveDesktop();
1121     }
1122 
1123     const BrowserList* browser_list =
1124         BrowserList::GetInstance(host_desktop_type);
1125     for (BrowserList::const_iterator it = browser_list->begin();
1126          it != browser_list->end(); ++it) {
1127       if ((*it)->type() == Browser::TYPE_TABBED) {
1128         (*it)->OpenURL(params);
1129         break;
1130       }
1131     }
1132   }
1133 }
1134 
SaveToFile(const std::string & url,const std::string & content,bool save_as)1135 void DevToolsWindow::SaveToFile(const std::string& url,
1136                                 const std::string& content,
1137                                 bool save_as) {
1138   file_helper_->Save(url, content, save_as,
1139                      base::Bind(&DevToolsWindow::FileSavedAs,
1140                                 weak_factory_.GetWeakPtr(), url),
1141                      base::Bind(&DevToolsWindow::CanceledFileSaveAs,
1142                                 weak_factory_.GetWeakPtr(), url));
1143 }
1144 
AppendToFile(const std::string & url,const std::string & content)1145 void DevToolsWindow::AppendToFile(const std::string& url,
1146                                   const std::string& content) {
1147   file_helper_->Append(url, content,
1148                        base::Bind(&DevToolsWindow::AppendedTo,
1149                                   weak_factory_.GetWeakPtr(), url));
1150 }
1151 
RequestFileSystems()1152 void DevToolsWindow::RequestFileSystems() {
1153   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1154   file_helper_->RequestFileSystems(base::Bind(
1155       &DevToolsWindow::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
1156 }
1157 
AddFileSystem()1158 void DevToolsWindow::AddFileSystem() {
1159   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1160   file_helper_->AddFileSystem(
1161       base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1162       base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1163                  weak_factory_.GetWeakPtr()));
1164 }
1165 
RemoveFileSystem(const std::string & file_system_path)1166 void DevToolsWindow::RemoveFileSystem(const std::string& file_system_path) {
1167   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1168   file_helper_->RemoveFileSystem(file_system_path);
1169   StringValue file_system_path_value(file_system_path);
1170   CallClientFunction("InspectorFrontendAPI.fileSystemRemoved",
1171                      &file_system_path_value, NULL, NULL);
1172 }
1173 
UpgradeDraggedFileSystemPermissions(const std::string & file_system_url)1174 void DevToolsWindow::UpgradeDraggedFileSystemPermissions(
1175     const std::string& file_system_url) {
1176   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1177   file_helper_->UpgradeDraggedFileSystemPermissions(
1178       file_system_url,
1179       base::Bind(&DevToolsWindow::FileSystemAdded, weak_factory_.GetWeakPtr()),
1180       base::Bind(&DevToolsWindow::ShowDevToolsConfirmInfoBar,
1181                  weak_factory_.GetWeakPtr()));
1182 }
1183 
IndexPath(int request_id,const std::string & file_system_path)1184 void DevToolsWindow::IndexPath(int request_id,
1185                                const std::string& file_system_path) {
1186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1187   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1188   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1189     IndexingDone(request_id, file_system_path);
1190     return;
1191   }
1192   indexing_jobs_[request_id] =
1193       scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
1194           file_system_indexer_->IndexPath(
1195               file_system_path,
1196               Bind(&DevToolsWindow::IndexingTotalWorkCalculated,
1197                    weak_factory_.GetWeakPtr(),
1198                    request_id,
1199                    file_system_path),
1200               Bind(&DevToolsWindow::IndexingWorked,
1201                    weak_factory_.GetWeakPtr(),
1202                    request_id,
1203                    file_system_path),
1204               Bind(&DevToolsWindow::IndexingDone,
1205                    weak_factory_.GetWeakPtr(),
1206                    request_id,
1207                    file_system_path)));
1208 }
1209 
StopIndexing(int request_id)1210 void DevToolsWindow::StopIndexing(int request_id) {
1211   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1212   IndexingJobsMap::iterator it = indexing_jobs_.find(request_id);
1213   if (it == indexing_jobs_.end())
1214     return;
1215   it->second->Stop();
1216   indexing_jobs_.erase(it);
1217 }
1218 
SearchInPath(int request_id,const std::string & file_system_path,const std::string & query)1219 void DevToolsWindow::SearchInPath(int request_id,
1220                                   const std::string& file_system_path,
1221                                   const std::string& query) {
1222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1223   CHECK(web_contents_->GetURL().SchemeIs(chrome::kChromeDevToolsScheme));
1224   if (!file_helper_->IsFileSystemAdded(file_system_path)) {
1225     SearchCompleted(request_id, file_system_path, std::vector<std::string>());
1226     return;
1227   }
1228   file_system_indexer_->SearchInPath(file_system_path,
1229                                      query,
1230                                      Bind(&DevToolsWindow::SearchCompleted,
1231                                           weak_factory_.GetWeakPtr(),
1232                                           request_id,
1233                                           file_system_path));
1234 }
1235 
FileSavedAs(const std::string & url)1236 void DevToolsWindow::FileSavedAs(const std::string& url) {
1237   StringValue url_value(url);
1238   CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
1239 }
1240 
CanceledFileSaveAs(const std::string & url)1241 void DevToolsWindow::CanceledFileSaveAs(const std::string& url) {
1242   StringValue url_value(url);
1243   CallClientFunction("InspectorFrontendAPI.canceledSaveURL",
1244                      &url_value, NULL, NULL);
1245 }
1246 
AppendedTo(const std::string & url)1247 void DevToolsWindow::AppendedTo(const std::string& url) {
1248   StringValue url_value(url);
1249   CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL,
1250                      NULL);
1251 }
1252 
FileSystemsLoaded(const std::vector<DevToolsFileHelper::FileSystem> & file_systems)1253 void DevToolsWindow::FileSystemsLoaded(
1254     const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
1255   ListValue file_systems_value;
1256   for (size_t i = 0; i < file_systems.size(); ++i)
1257     file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
1258   CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded",
1259                      &file_systems_value, NULL, NULL);
1260 }
1261 
FileSystemAdded(const DevToolsFileHelper::FileSystem & file_system)1262 void DevToolsWindow::FileSystemAdded(
1263     const DevToolsFileHelper::FileSystem& file_system) {
1264   scoped_ptr<base::StringValue> error_string_value(
1265       new base::StringValue(std::string()));
1266   scoped_ptr<base::DictionaryValue> file_system_value;
1267   if (!file_system.file_system_path.empty())
1268     file_system_value.reset(CreateFileSystemValue(file_system));
1269   CallClientFunction("InspectorFrontendAPI.fileSystemAdded",
1270                      error_string_value.get(), file_system_value.get(), NULL);
1271 }
1272 
IndexingTotalWorkCalculated(int request_id,const std::string & file_system_path,int total_work)1273 void DevToolsWindow::IndexingTotalWorkCalculated(
1274     int request_id,
1275     const std::string& file_system_path,
1276     int total_work) {
1277   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1278   base::FundamentalValue request_id_value(request_id);
1279   StringValue file_system_path_value(file_system_path);
1280   base::FundamentalValue total_work_value(total_work);
1281   CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated",
1282                      &request_id_value, &file_system_path_value,
1283                      &total_work_value);
1284 }
1285 
IndexingWorked(int request_id,const std::string & file_system_path,int worked)1286 void DevToolsWindow::IndexingWorked(int request_id,
1287                                     const std::string& file_system_path,
1288                                     int worked) {
1289   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1290   base::FundamentalValue request_id_value(request_id);
1291   StringValue file_system_path_value(file_system_path);
1292   base::FundamentalValue worked_value(worked);
1293   CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value,
1294                      &file_system_path_value, &worked_value);
1295 }
1296 
IndexingDone(int request_id,const std::string & file_system_path)1297 void DevToolsWindow::IndexingDone(int request_id,
1298                                   const std::string& file_system_path) {
1299   indexing_jobs_.erase(request_id);
1300   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1301   base::FundamentalValue request_id_value(request_id);
1302   StringValue file_system_path_value(file_system_path);
1303   CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value,
1304                      &file_system_path_value, NULL);
1305 }
1306 
SearchCompleted(int request_id,const std::string & file_system_path,const std::vector<std::string> & file_paths)1307 void DevToolsWindow::SearchCompleted(
1308     int request_id,
1309     const std::string& file_system_path,
1310     const std::vector<std::string>& file_paths) {
1311   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1312   ListValue file_paths_value;
1313   for (std::vector<std::string>::const_iterator it(file_paths.begin());
1314        it != file_paths.end(); ++it) {
1315     file_paths_value.AppendString(*it);
1316   }
1317   base::FundamentalValue request_id_value(request_id);
1318   StringValue file_system_path_value(file_system_path);
1319   CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value,
1320                      &file_system_path_value, &file_paths_value);
1321 }
1322 
ShowDevToolsConfirmInfoBar(const base::string16 & message,const InfoBarCallback & callback)1323 void DevToolsWindow::ShowDevToolsConfirmInfoBar(
1324     const base::string16& message,
1325     const InfoBarCallback& callback) {
1326   DevToolsConfirmInfoBarDelegate::Create(
1327       IsDocked() ?
1328           InfoBarService::FromWebContents(GetInspectedWebContents()) :
1329           InfoBarService::FromWebContents(web_contents_),
1330       callback, message);
1331 }
1332 
CreateDevToolsBrowser()1333 void DevToolsWindow::CreateDevToolsBrowser() {
1334   std::string wp_key = GetDevToolsWindowPlacementPrefKey();
1335   PrefService* prefs = profile_->GetPrefs();
1336   const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
1337   if (!wp_pref || wp_pref->empty()) {
1338     DictionaryPrefUpdate update(prefs, wp_key.c_str());
1339     DictionaryValue* defaults = update.Get();
1340     defaults->SetInteger("left", 100);
1341     defaults->SetInteger("top", 100);
1342     defaults->SetInteger("right", 740);
1343     defaults->SetInteger("bottom", 740);
1344     defaults->SetBoolean("maximized", false);
1345     defaults->SetBoolean("always_on_top", false);
1346   }
1347 
1348   browser_ = new Browser(Browser::CreateParams::CreateForDevTools(
1349       profile_,
1350       chrome::GetHostDesktopTypeForNativeView(
1351           web_contents_->GetView()->GetNativeView())));
1352   browser_->tab_strip_model()->AddWebContents(
1353       web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1354       TabStripModel::ADD_ACTIVE);
1355   GetRenderViewHost()->SyncRendererPrefs();
1356 }
1357 
1358 // static
FindInspectedBrowserAndTabIndex(content::WebContents * inspected_web_contents,Browser ** browser,int * tab)1359 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(
1360     content::WebContents* inspected_web_contents, Browser** browser, int* tab) {
1361   if (!inspected_web_contents)
1362     return false;
1363 
1364   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1365     int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
1366         inspected_web_contents);
1367     if (tab_index != TabStripModel::kNoTab) {
1368       *browser = *it;
1369       *tab = tab_index;
1370       return true;
1371     }
1372   }
1373   return false;
1374 }
1375 
GetInspectedBrowserWindow()1376 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
1377   Browser* browser = NULL;
1378   int tab;
1379   return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1380                                          &browser, &tab) ?
1381       browser->window() : NULL;
1382 }
1383 
IsInspectedBrowserPopup()1384 bool DevToolsWindow::IsInspectedBrowserPopup() {
1385   Browser* browser = NULL;
1386   int tab;
1387   return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
1388                                          &browser, &tab) &&
1389       browser->is_type_popup();
1390 }
1391 
UpdateFrontendDockSide()1392 void DevToolsWindow::UpdateFrontendDockSide() {
1393   base::StringValue dock_side(SideToString(dock_side_));
1394   CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side, NULL,
1395                      NULL);
1396   base::FundamentalValue docked(IsDocked());
1397   CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked, NULL,
1398                      NULL);
1399 }
1400 
ScheduleAction(const DevToolsToggleAction & action)1401 void DevToolsWindow::ScheduleAction(const DevToolsToggleAction& action) {
1402   action_on_load_ = action;
1403   if (is_loaded_)
1404     DoAction();
1405 }
1406 
DoAction()1407 void DevToolsWindow::DoAction() {
1408   UpdateFrontendDockSide();
1409   switch (action_on_load_.type()) {
1410     case DevToolsToggleAction::kShowConsole:
1411       CallClientFunction("InspectorFrontendAPI.showConsole", NULL, NULL, NULL);
1412       break;
1413 
1414     case DevToolsToggleAction::kInspect:
1415       CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL,
1416                          NULL, NULL);
1417       break;
1418 
1419     case DevToolsToggleAction::kShow:
1420     case DevToolsToggleAction::kToggle:
1421       // Do nothing.
1422       break;
1423 
1424     case DevToolsToggleAction::kReveal: {
1425       const DevToolsToggleAction::RevealParams* params =
1426           action_on_load_.params();
1427       CHECK(params);
1428       base::StringValue url_value(params->url);
1429       base::FundamentalValue line_value(static_cast<int>(params->line_number));
1430       base::FundamentalValue column_value(
1431           static_cast<int>(params->column_number));
1432       CallClientFunction("InspectorFrontendAPI.revealSourceLine",
1433                          &url_value,
1434                          &line_value,
1435                          &column_value);
1436       break;
1437     }
1438     default:
1439       NOTREACHED();
1440       break;
1441   }
1442   action_on_load_ = DevToolsToggleAction::Show();
1443 }
1444 
UpdateTheme()1445 void DevToolsWindow::UpdateTheme() {
1446   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
1447   DCHECK(tp);
1448 
1449   std::string command("InspectorFrontendAPI.setToolbarColors(\"" +
1450       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) +
1451       "\", \"" +
1452       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) +
1453       "\")");
1454   web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1455       base::string16(), base::ASCIIToUTF16(command));
1456 }
1457 
AddDevToolsExtensionsToClient()1458 void DevToolsWindow::AddDevToolsExtensionsToClient() {
1459   content::WebContents* inspected_web_contents = GetInspectedWebContents();
1460   if (inspected_web_contents) {
1461     SessionTabHelper* session_tab_helper =
1462         SessionTabHelper::FromWebContents(inspected_web_contents);
1463     if (session_tab_helper) {
1464       base::FundamentalValue tabId(session_tab_helper->session_id().id());
1465       CallClientFunction("WebInspector.setInspectedTabId", &tabId, NULL, NULL);
1466     }
1467   }
1468 
1469   Profile* profile =
1470       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
1471   const ExtensionService* extension_service = extensions::ExtensionSystem::Get(
1472       profile->GetOriginalProfile())->extension_service();
1473   if (!extension_service)
1474     return;
1475   const ExtensionSet* extensions = extension_service->extensions();
1476 
1477   ListValue results;
1478   for (ExtensionSet::const_iterator extension(extensions->begin());
1479        extension != extensions->end(); ++extension) {
1480     if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty())
1481       continue;
1482     DictionaryValue* extension_info = new DictionaryValue();
1483     extension_info->Set(
1484         "startPage",
1485         new StringValue(
1486             extensions::ManifestURL::GetDevToolsPage(extension->get()).spec()));
1487     extension_info->Set("name", new StringValue((*extension)->name()));
1488     extension_info->Set(
1489         "exposeExperimentalAPIs",
1490         new base::FundamentalValue((*extension)->HasAPIPermission(
1491             extensions::APIPermission::kExperimental)));
1492     results.Append(extension_info);
1493   }
1494   CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL);
1495 }
1496 
CallClientFunction(const std::string & function_name,const Value * arg1,const Value * arg2,const Value * arg3)1497 void DevToolsWindow::CallClientFunction(const std::string& function_name,
1498                                         const Value* arg1,
1499                                         const Value* arg2,
1500                                         const Value* arg3) {
1501   std::string params;
1502   if (arg1) {
1503     std::string json;
1504     base::JSONWriter::Write(arg1, &json);
1505     params.append(json);
1506     if (arg2) {
1507       base::JSONWriter::Write(arg2, &json);
1508       params.append(", " + json);
1509       if (arg3) {
1510         base::JSONWriter::Write(arg3, &json);
1511         params.append(", " + json);
1512       }
1513     }
1514   }
1515   base::string16 javascript = ASCIIToUTF16(function_name + "(" + params + ");");
1516   web_contents_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
1517       base::string16(), javascript);
1518 }
1519 
UpdateBrowserToolbar()1520 void DevToolsWindow::UpdateBrowserToolbar() {
1521   BrowserWindow* inspected_window = GetInspectedBrowserWindow();
1522   if (inspected_window)
1523     inspected_window->UpdateToolbar(NULL);
1524 }
1525 
IsDocked()1526 bool DevToolsWindow::IsDocked() {
1527   return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED;
1528 }
1529 
Restore()1530 void DevToolsWindow::Restore() {
1531   if (dock_side_ == DEVTOOLS_DOCK_SIDE_MINIMIZED)
1532     SetDockSide(SideToString(dock_side_before_minimized_));
1533 }
1534 
GetInspectedWebContents()1535 content::WebContents* DevToolsWindow::GetInspectedWebContents() {
1536   return inspected_contents_observer_ ?
1537       inspected_contents_observer_->web_contents() : NULL;
1538 }
1539 
DocumentOnLoadCompletedInMainFrame()1540 void DevToolsWindow::DocumentOnLoadCompletedInMainFrame() {
1541   is_loaded_ = true;
1542   UpdateTheme();
1543   DoAction();
1544   AddDevToolsExtensionsToClient();
1545 }
1546