• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/command_line.h"
6 #include "base/json/json_writer.h"
7 #include "base/string_number_conversions.h"
8 #include "base/utf_string_conversions.h"
9 #include "base/values.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/debugger/devtools_manager.h"
12 #include "chrome/browser/debugger/devtools_window.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/load_notification_details.h"
15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/browser/prefs/scoped_user_pref_update.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/tabs/tab_strip_model.h"
19 #include "chrome/browser/themes/theme_service.h"
20 #include "chrome/browser/themes/theme_service_factory.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_list.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/render_messages.h"
27 #include "chrome/common/url_constants.h"
28 #include "content/browser/in_process_webkit/session_storage_namespace.h"
29 #include "content/browser/renderer_host/render_view_host.h"
30 #include "content/browser/tab_contents/navigation_controller.h"
31 #include "content/browser/tab_contents/navigation_entry.h"
32 #include "content/browser/tab_contents/tab_contents.h"
33 #include "content/browser/tab_contents/tab_contents_view.h"
34 #include "content/common/bindings_policy.h"
35 #include "content/common/notification_service.h"
36 #include "grit/generated_resources.h"
37 
38 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
39 
40 // static
GetDevToolsContents(TabContents * inspected_tab)41 TabContentsWrapper* DevToolsWindow::GetDevToolsContents(
42     TabContents* inspected_tab) {
43   if (!inspected_tab) {
44     return NULL;
45   }
46 
47   if (!DevToolsManager::GetInstance())
48     return NULL;  // Happens only in tests.
49 
50   DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
51           GetDevToolsClientHostFor(inspected_tab->render_view_host());
52   if (!client_host) {
53     return NULL;
54   }
55 
56   DevToolsWindow* window = client_host->AsDevToolsWindow();
57   if (!window || !window->is_docked()) {
58     return NULL;
59   }
60   return window->tab_contents();
61 }
62 
DevToolsWindow(Profile * profile,RenderViewHost * inspected_rvh,bool docked)63 DevToolsWindow::DevToolsWindow(Profile* profile,
64                                RenderViewHost* inspected_rvh,
65                                bool docked)
66     : profile_(profile),
67       browser_(NULL),
68       docked_(docked),
69       is_loaded_(false),
70       action_on_load_(DEVTOOLS_TOGGLE_ACTION_NONE) {
71   // Create TabContents with devtools.
72   tab_contents_ =
73       Browser::TabContentsFactory(profile, NULL, MSG_ROUTING_NONE, NULL, NULL);
74   tab_contents_->tab_contents()->
75       render_view_host()->AllowBindings(BindingsPolicy::WEB_UI);
76   tab_contents_->controller().LoadURL(
77       GetDevToolsUrl(), GURL(), PageTransition::START_PAGE);
78 
79   // Wipe out page icon so that the default application icon is used.
80   NavigationEntry* entry = tab_contents_->controller().GetActiveEntry();
81   entry->favicon().set_bitmap(SkBitmap());
82   entry->favicon().set_is_valid(true);
83 
84   // Register on-load actions.
85   registrar_.Add(this,
86                  NotificationType::LOAD_STOP,
87                  Source<NavigationController>(&tab_contents_->controller()));
88   registrar_.Add(this,
89                  NotificationType::TAB_CLOSING,
90                  Source<NavigationController>(&tab_contents_->controller()));
91   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
92                  NotificationService::AllSources());
93   inspected_tab_ = inspected_rvh->delegate()->GetAsTabContents();
94 }
95 
~DevToolsWindow()96 DevToolsWindow::~DevToolsWindow() {
97 }
98 
AsDevToolsWindow()99 DevToolsWindow* DevToolsWindow::AsDevToolsWindow() {
100   return this;
101 }
102 
SendMessageToClient(const IPC::Message & message)103 void DevToolsWindow::SendMessageToClient(const IPC::Message& message) {
104   RenderViewHost* target_host =
105       tab_contents_->tab_contents()->render_view_host();
106   IPC::Message* m =  new IPC::Message(message);
107   m->set_routing_id(target_host->routing_id());
108   target_host->Send(m);
109 }
110 
InspectedTabClosing()111 void DevToolsWindow::InspectedTabClosing() {
112   if (docked_) {
113     // Update dev tools to reflect removed dev tools window.
114 
115     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
116     if (inspected_window)
117       inspected_window->UpdateDevTools();
118     // In case of docked tab_contents we own it, so delete here.
119     delete tab_contents_;
120 
121     delete this;
122   } else {
123     // First, initiate self-destruct to free all the registrars.
124     // Then close all tabs. Browser will take care of deleting tab_contents
125     // for us.
126     Browser* browser = browser_;
127     delete this;
128     browser->CloseAllTabs();
129   }
130 }
131 
TabReplaced(TabContentsWrapper * new_tab)132 void DevToolsWindow::TabReplaced(TabContentsWrapper* new_tab) {
133   DCHECK_EQ(profile_, new_tab->profile());
134   inspected_tab_ = new_tab->tab_contents();
135 }
136 
Show(DevToolsToggleAction action)137 void DevToolsWindow::Show(DevToolsToggleAction action) {
138   if (docked_) {
139     Browser* inspected_browser;
140     int inspected_tab_index;
141     // Tell inspected browser to update splitter and switch to inspected panel.
142     if (!IsInspectedBrowserPopup() &&
143         FindInspectedBrowserAndTabIndex(&inspected_browser,
144                                         &inspected_tab_index)) {
145       BrowserWindow* inspected_window = inspected_browser->window();
146       tab_contents_->tab_contents()->set_delegate(this);
147       inspected_window->UpdateDevTools();
148       tab_contents_->view()->SetInitialFocus();
149       inspected_window->Show();
150       TabStripModel* tabstrip_model = inspected_browser->tabstrip_model();
151       tabstrip_model->ActivateTabAt(inspected_tab_index, true);
152       ScheduleAction(action);
153       return;
154     } else {
155       // Sometimes we don't know where to dock. Stay undocked.
156       docked_ = false;
157     }
158   }
159 
160   // Avoid consecutive window switching if the devtools window has been opened
161   // and the Inspect Element shortcut is pressed in the inspected tab.
162   bool should_show_window =
163       !browser_ || action != DEVTOOLS_TOGGLE_ACTION_INSPECT;
164 
165   if (!browser_)
166     CreateDevToolsBrowser();
167 
168   if (should_show_window) {
169     browser_->window()->Show();
170     tab_contents_->view()->SetInitialFocus();
171   }
172 
173   ScheduleAction(action);
174 }
175 
Activate()176 void DevToolsWindow::Activate() {
177   if (!docked_) {
178     if (!browser_->window()->IsActive()) {
179       browser_->window()->Activate();
180     }
181   } else {
182     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
183     if (inspected_window)
184       tab_contents_->view()->Focus();
185   }
186 }
187 
SetDocked(bool docked)188 void DevToolsWindow::SetDocked(bool docked) {
189   if (docked_ == docked)
190     return;
191   if (docked && (!GetInspectedBrowserWindow() || IsInspectedBrowserPopup())) {
192     // Cannot dock, avoid window flashing due to close-reopen cycle.
193     return;
194   }
195   docked_ = docked;
196 
197   if (docked) {
198     // Detach window from the external devtools browser. It will lead to
199     // the browser object's close and delete. Remove observer first.
200     TabStripModel* tabstrip_model = browser_->tabstrip_model();
201     tabstrip_model->DetachTabContentsAt(
202         tabstrip_model->GetIndexOfTabContents(tab_contents_));
203     browser_ = NULL;
204   } else {
205     // Update inspected window to hide split and reset it.
206     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
207     if (inspected_window) {
208       inspected_window->UpdateDevTools();
209       inspected_window = NULL;
210     }
211   }
212   Show(DEVTOOLS_TOGGLE_ACTION_NONE);
213 }
214 
GetRenderViewHost()215 RenderViewHost* DevToolsWindow::GetRenderViewHost() {
216   return tab_contents_->render_view_host();
217 }
218 
CreateDevToolsBrowser()219 void DevToolsWindow::CreateDevToolsBrowser() {
220   // TODO(pfeldman): Make browser's getter for this key static.
221   std::string wp_key;
222   wp_key.append(prefs::kBrowserWindowPlacement);
223   wp_key.append("_");
224   wp_key.append(kDevToolsApp);
225 
226   PrefService* prefs = profile_->GetPrefs();
227   if (!prefs->FindPreference(wp_key.c_str())) {
228     prefs->RegisterDictionaryPref(wp_key.c_str());
229   }
230 
231   const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str());
232   if (!wp_pref || wp_pref->empty()) {
233     DictionaryPrefUpdate update(prefs, wp_key.c_str());
234     DictionaryValue* defaults = update.Get();
235     defaults->SetInteger("left", 100);
236     defaults->SetInteger("top", 100);
237     defaults->SetInteger("right", 740);
238     defaults->SetInteger("bottom", 740);
239     defaults->SetBoolean("maximized", false);
240     defaults->SetBoolean("always_on_top", false);
241   }
242 
243   browser_ = Browser::CreateForDevTools(profile_);
244   browser_->tabstrip_model()->AddTabContents(
245       tab_contents_, -1, PageTransition::START_PAGE, TabStripModel::ADD_ACTIVE);
246 }
247 
FindInspectedBrowserAndTabIndex(Browser ** browser,int * tab)248 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(Browser** browser,
249                                                      int* tab) {
250   const NavigationController& controller = inspected_tab_->controller();
251   for (BrowserList::const_iterator it = BrowserList::begin();
252        it != BrowserList::end(); ++it) {
253     int tab_index = (*it)->GetIndexOfController(&controller);
254     if (tab_index != TabStripModel::kNoTab) {
255       *browser = *it;
256       *tab = tab_index;
257       return true;
258     }
259   }
260   return false;
261 }
262 
GetInspectedBrowserWindow()263 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
264   Browser* browser = NULL;
265   int tab;
266   return FindInspectedBrowserAndTabIndex(&browser, &tab) ?
267       browser->window() : NULL;
268 }
269 
IsInspectedBrowserPopup()270 bool DevToolsWindow::IsInspectedBrowserPopup() {
271   Browser* browser = NULL;
272   int tab;
273   if (!FindInspectedBrowserAndTabIndex(&browser, &tab))
274     return false;
275 
276   return (browser->type() & Browser::TYPE_POPUP) != 0;
277 }
278 
UpdateFrontendAttachedState()279 void DevToolsWindow::UpdateFrontendAttachedState() {
280   tab_contents_->render_view_host()->ExecuteJavascriptInWebFrame(
281       string16(),
282       docked_ ? ASCIIToUTF16("WebInspector.setAttachedWindow(true);")
283               : ASCIIToUTF16("WebInspector.setAttachedWindow(false);"));
284 }
285 
286 
AddDevToolsExtensionsToClient()287 void DevToolsWindow::AddDevToolsExtensionsToClient() {
288   if (inspected_tab_) {
289     FundamentalValue tabId(inspected_tab_->controller().session_id().id());
290     CallClientFunction(ASCIIToUTF16("WebInspector.setInspectedTabId"), tabId);
291   }
292   ListValue results;
293   const ExtensionService* extension_service =
294       tab_contents_->tab_contents()->profile()->
295           GetOriginalProfile()->GetExtensionService();
296   if (!extension_service)
297     return;
298 
299   const ExtensionList* extensions = extension_service->extensions();
300 
301   for (ExtensionList::const_iterator extension = extensions->begin();
302        extension != extensions->end(); ++extension) {
303     if ((*extension)->devtools_url().is_empty())
304       continue;
305     DictionaryValue* extension_info = new DictionaryValue();
306     extension_info->Set("startPage",
307         new StringValue((*extension)->devtools_url().spec()));
308     results.Append(extension_info);
309   }
310   CallClientFunction(ASCIIToUTF16("WebInspector.addExtensions"), results);
311 }
312 
OpenURLFromTab(TabContents * source,const GURL & url,const GURL & referrer,WindowOpenDisposition disposition,PageTransition::Type transition)313 void DevToolsWindow::OpenURLFromTab(TabContents* source,
314                                     const GURL& url,
315                                     const GURL& referrer,
316                                     WindowOpenDisposition disposition,
317                                     PageTransition::Type transition) {
318   if (inspected_tab_)
319     inspected_tab_->OpenURL(url,
320                             GURL(),
321                             NEW_FOREGROUND_TAB,
322                             PageTransition::LINK);
323 }
324 
CallClientFunction(const string16 & function_name,const Value & arg)325 void DevToolsWindow::CallClientFunction(const string16& function_name,
326                                         const Value& arg) {
327   std::string json;
328   base::JSONWriter::Write(&arg, false, &json);
329   string16 javascript = function_name + char16('(') + UTF8ToUTF16(json) +
330       ASCIIToUTF16(");");
331   tab_contents_->render_view_host()->
332       ExecuteJavascriptInWebFrame(string16(), javascript);
333 }
334 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)335 void DevToolsWindow::Observe(NotificationType type,
336                              const NotificationSource& source,
337                              const NotificationDetails& details) {
338   if (type == NotificationType::LOAD_STOP && !is_loaded_) {
339     is_loaded_ = true;
340     UpdateTheme();
341     DoAction();
342     AddDevToolsExtensionsToClient();
343   } else if (type == NotificationType::TAB_CLOSING) {
344     if (Source<NavigationController>(source).ptr() ==
345             &tab_contents_->controller()) {
346       // This happens when browser closes all of its tabs as a result
347       // of window.Close event.
348       // Notify manager that this DevToolsClientHost no longer exists and
349       // initiate self-destuct here.
350       NotifyCloseListener();
351       delete this;
352     }
353   } else if (type == NotificationType::BROWSER_THEME_CHANGED) {
354     UpdateTheme();
355   }
356 }
357 
ScheduleAction(DevToolsToggleAction action)358 void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) {
359   action_on_load_ = action;
360   if (is_loaded_)
361     DoAction();
362 }
363 
DoAction()364 void DevToolsWindow::DoAction() {
365   UpdateFrontendAttachedState();
366   // TODO: these messages should be pushed through the WebKit API instead.
367   switch (action_on_load_) {
368     case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE:
369       tab_contents_->render_view_host()->ExecuteJavascriptInWebFrame(
370           string16(), ASCIIToUTF16("WebInspector.showConsole();"));
371       break;
372     case DEVTOOLS_TOGGLE_ACTION_INSPECT:
373       tab_contents_->render_view_host()->ExecuteJavascriptInWebFrame(
374           string16(), ASCIIToUTF16("WebInspector.toggleSearchingForNode();"));
375     case DEVTOOLS_TOGGLE_ACTION_NONE:
376       // Do nothing.
377       break;
378     default:
379       NOTREACHED();
380   }
381   action_on_load_ = DEVTOOLS_TOGGLE_ACTION_NONE;
382 }
383 
SkColorToRGBAString(SkColor color)384 std::string SkColorToRGBAString(SkColor color) {
385   // We convert the alpha using DoubleToString because StringPrintf will use
386   // locale specific formatters (e.g., use , instead of . in German).
387   return StringPrintf("rgba(%d,%d,%d,%s)", SkColorGetR(color),
388       SkColorGetG(color), SkColorGetB(color),
389       base::DoubleToString(SkColorGetA(color) / 255.0).c_str());
390 }
391 
GetDevToolsUrl()392 GURL DevToolsWindow::GetDevToolsUrl() {
393   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
394   CHECK(tp);
395 
396   SkColor color_toolbar =
397       tp->GetColor(ThemeService::COLOR_TOOLBAR);
398   SkColor color_tab_text =
399       tp->GetColor(ThemeService::COLOR_BOOKMARK_TEXT);
400 
401   std::string url_string = StringPrintf(
402       "%sdevtools.html?docked=%s&toolbar_color=%s&text_color=%s",
403       chrome::kChromeUIDevToolsURL,
404       docked_ ? "true" : "false",
405       SkColorToRGBAString(color_toolbar).c_str(),
406       SkColorToRGBAString(color_tab_text).c_str());
407   return GURL(url_string);
408 }
409 
UpdateTheme()410 void DevToolsWindow::UpdateTheme() {
411   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_);
412   CHECK(tp);
413 
414   SkColor color_toolbar =
415       tp->GetColor(ThemeService::COLOR_TOOLBAR);
416   SkColor color_tab_text =
417       tp->GetColor(ThemeService::COLOR_BOOKMARK_TEXT);
418   std::string command = StringPrintf(
419       "WebInspector.setToolbarColors(\"%s\", \"%s\")",
420       SkColorToRGBAString(color_toolbar).c_str(),
421       SkColorToRGBAString(color_tab_text).c_str());
422   tab_contents_->render_view_host()->
423       ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(command));
424 }
425 
AddNewContents(TabContents * source,TabContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture)426 void DevToolsWindow::AddNewContents(TabContents* source,
427                                     TabContents* new_contents,
428                                     WindowOpenDisposition disposition,
429                                     const gfx::Rect& initial_pos,
430                                     bool user_gesture) {
431   inspected_tab_->delegate()->AddNewContents(source,
432                                              new_contents,
433                                              disposition,
434                                              initial_pos,
435                                              user_gesture);
436 }
437 
CanReloadContents(TabContents * source) const438 bool DevToolsWindow::CanReloadContents(TabContents* source) const {
439   return false;
440 }
441 
PreHandleKeyboardEvent(const NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)442 bool DevToolsWindow::PreHandleKeyboardEvent(
443     const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
444   if (docked_) {
445     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
446     if (inspected_window)
447       return inspected_window->PreHandleKeyboardEvent(
448           event, is_keyboard_shortcut);
449   }
450   return false;
451 }
452 
HandleKeyboardEvent(const NativeWebKeyboardEvent & event)453 void DevToolsWindow::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
454   if (docked_) {
455     BrowserWindow* inspected_window = GetInspectedBrowserWindow();
456     if (inspected_window)
457       inspected_window->HandleKeyboardEvent(event);
458   }
459 }
460