OLD | NEW |
| (Empty) |
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 <algorithm> | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/json/json_writer.h" | |
9 #include "base/lazy_instance.h" | |
10 #include "base/string_number_conversions.h" | |
11 #include "base/stringprintf.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "base/values.h" | |
14 #include "chrome/browser/browser_process.h" | |
15 #include "chrome/browser/debugger/devtools_window.h" | |
16 #include "chrome/browser/extensions/api/debugger/debugger_api.h" | |
17 #include "chrome/browser/extensions/extension_service.h" | |
18 #include "chrome/browser/extensions/extension_system.h" | |
19 #include "chrome/browser/file_select_helper.h" | |
20 #include "chrome/browser/prefs/pref_service.h" | |
21 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
22 #include "chrome/browser/profiles/profile.h" | |
23 #include "chrome/browser/sessions/session_tab_helper.h" | |
24 #include "chrome/browser/themes/theme_service.h" | |
25 #include "chrome/browser/themes/theme_service_factory.h" | |
26 #include "chrome/browser/ui/browser.h" | |
27 #include "chrome/browser/ui/browser_list.h" | |
28 #include "chrome/browser/ui/browser_list_impl.h" | |
29 #include "chrome/browser/ui/browser_window.h" | |
30 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
31 #include "chrome/common/chrome_notification_types.h" | |
32 #include "chrome/common/chrome_switches.h" | |
33 #include "chrome/common/pref_names.h" | |
34 #include "chrome/common/render_messages.h" | |
35 #include "chrome/common/url_constants.h" | |
36 #include "content/public/browser/content_browser_client.h" | |
37 #include "content/public/browser/devtools_agent_host_registry.h" | |
38 #include "content/public/browser/devtools_manager.h" | |
39 #include "content/public/browser/favicon_status.h" | |
40 #include "content/public/browser/load_notification_details.h" | |
41 #include "content/public/browser/navigation_controller.h" | |
42 #include "content/public/browser/navigation_entry.h" | |
43 #include "content/public/browser/notification_source.h" | |
44 #include "content/public/browser/render_process_host.h" | |
45 #include "content/public/browser/render_view_host.h" | |
46 #include "content/public/browser/web_contents.h" | |
47 #include "content/public/browser/web_contents_view.h" | |
48 #include "content/public/common/bindings_policy.h" | |
49 #include "content/public/common/page_transition_types.h" | |
50 #include "grit/generated_resources.h" | |
51 | |
52 typedef std::vector<DevToolsWindow*> DevToolsWindowList; | |
53 namespace { | |
54 base::LazyInstance<DevToolsWindowList>::Leaky | |
55 g_instances = LAZY_INSTANCE_INITIALIZER; | |
56 } // namespace | |
57 | |
58 using content::DevToolsAgentHost; | |
59 using content::DevToolsAgentHostRegistry; | |
60 using content::DevToolsClientHost; | |
61 using content::DevToolsManager; | |
62 using content::FileChooserParams; | |
63 using content::NativeWebKeyboardEvent; | |
64 using content::NavigationController; | |
65 using content::NavigationEntry; | |
66 using content::OpenURLParams; | |
67 using content::RenderViewHost; | |
68 using content::WebContents; | |
69 | |
70 const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp"; | |
71 | |
72 const char kOldPrefBottom[] = "bottom"; | |
73 const char kOldPrefRight[] = "right"; | |
74 | |
75 const char kPrefBottom[] = "dock_bottom"; | |
76 const char kPrefRight[] = "dock_right"; | |
77 const char kPrefUndocked[] = "undocked"; | |
78 | |
79 const char kDockSideBottom[] = "bottom"; | |
80 const char kDockSideRight[] = "right"; | |
81 const char kDockSideUndocked[] = "undocked"; | |
82 | |
83 // Minimal height of devtools pane or content pane when devtools are docked | |
84 // to the browser window. | |
85 const int kMinDevToolsHeight = 50; | |
86 const int kMinDevToolsWidth = 150; | |
87 const int kMinContentsSize = 50; | |
88 | |
89 // static | |
90 void DevToolsWindow::RegisterUserPrefs(PrefService* prefs) { | |
91 prefs->RegisterBooleanPref(prefs::kDevToolsOpenDocked, | |
92 true, | |
93 PrefService::UNSYNCABLE_PREF); | |
94 prefs->RegisterStringPref(prefs::kDevToolsDockSide, | |
95 kDockSideBottom, | |
96 PrefService::UNSYNCABLE_PREF); | |
97 prefs->RegisterDictionaryPref(prefs::kDevToolsEditedFiles, | |
98 PrefService::UNSYNCABLE_PREF); | |
99 } | |
100 | |
101 // static | |
102 DevToolsWindow* DevToolsWindow::GetDockedInstanceForInspectedTab( | |
103 WebContents* inspected_web_contents) { | |
104 if (!inspected_web_contents) | |
105 return NULL; | |
106 | |
107 if (!DevToolsAgentHostRegistry::HasDevToolsAgentHost( | |
108 inspected_web_contents->GetRenderViewHost())) | |
109 return NULL; | |
110 DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost( | |
111 inspected_web_contents->GetRenderViewHost()); | |
112 DevToolsManager* manager = DevToolsManager::GetInstance(); | |
113 DevToolsClientHost* client_host = manager->GetDevToolsClientHostFor(agent); | |
114 DevToolsWindow* window = AsDevToolsWindow(client_host); | |
115 return window && window->IsDocked() ? window : NULL; | |
116 } | |
117 | |
118 // static | |
119 bool DevToolsWindow::IsDevToolsWindow(RenderViewHost* window_rvh) { | |
120 return AsDevToolsWindow(window_rvh) != NULL; | |
121 } | |
122 | |
123 // static | |
124 DevToolsWindow* DevToolsWindow::OpenDevToolsWindowForWorker( | |
125 Profile* profile, | |
126 DevToolsAgentHost* worker_agent) { | |
127 DevToolsWindow* window; | |
128 DevToolsClientHost* client = content::DevToolsManager::GetInstance()-> | |
129 GetDevToolsClientHostFor(worker_agent); | |
130 if (client) { | |
131 window = AsDevToolsWindow(client); | |
132 if (!window) | |
133 return NULL; | |
134 } else { | |
135 window = DevToolsWindow::CreateDevToolsWindowForWorker(profile); | |
136 DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( | |
137 worker_agent, | |
138 window->frontend_host_); | |
139 } | |
140 window->Show(DEVTOOLS_TOGGLE_ACTION_SHOW); | |
141 return window; | |
142 } | |
143 | |
144 // static | |
145 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker( | |
146 Profile* profile) { | |
147 return Create(profile, NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true); | |
148 } | |
149 | |
150 // static | |
151 DevToolsWindow* DevToolsWindow::OpenDevToolsWindow( | |
152 RenderViewHost* inspected_rvh) { | |
153 return ToggleDevToolsWindow(inspected_rvh, true, | |
154 DEVTOOLS_TOGGLE_ACTION_SHOW); | |
155 } | |
156 | |
157 // static | |
158 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( | |
159 Browser* browser, | |
160 DevToolsToggleAction action) { | |
161 if (action == DEVTOOLS_TOGGLE_ACTION_TOGGLE && browser->is_devtools()) { | |
162 browser->tab_strip_model()->CloseAllTabs(); | |
163 return NULL; | |
164 } | |
165 RenderViewHost* inspected_rvh = | |
166 browser->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(); | |
167 | |
168 return ToggleDevToolsWindow(inspected_rvh, | |
169 action == DEVTOOLS_TOGGLE_ACTION_INSPECT, | |
170 action); | |
171 } | |
172 | |
173 void DevToolsWindow::InspectElement(RenderViewHost* inspected_rvh, | |
174 int x, | |
175 int y) { | |
176 DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost( | |
177 inspected_rvh); | |
178 DevToolsManager::GetInstance()->InspectElement(agent, x, y); | |
179 // TODO(loislo): we should initiate DevTools window opening from within | |
180 // renderer. Otherwise, we still can hit a race condition here. | |
181 OpenDevToolsWindow(inspected_rvh); | |
182 } | |
183 | |
184 | |
185 DevToolsWindow* DevToolsWindow::Create( | |
186 Profile* profile, | |
187 RenderViewHost* inspected_rvh, | |
188 DevToolsDockSide dock_side, | |
189 bool shared_worker_frontend) { | |
190 // Create WebContents with devtools. | |
191 WebContents* web_contents = | |
192 WebContents::Create(WebContents::CreateParams(profile)); | |
193 web_contents->GetRenderViewHost()->AllowBindings( | |
194 content::BINDINGS_POLICY_WEB_UI); | |
195 web_contents->GetController().LoadURL( | |
196 GetDevToolsUrl(profile, dock_side, shared_worker_frontend), | |
197 content::Referrer(), | |
198 content::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
199 std::string()); | |
200 return new DevToolsWindow(web_contents, profile, inspected_rvh, dock_side); | |
201 } | |
202 | |
203 DevToolsWindow::DevToolsWindow(WebContents* web_contents, | |
204 Profile* profile, | |
205 RenderViewHost* inspected_rvh, | |
206 DevToolsDockSide dock_side) | |
207 : profile_(profile), | |
208 inspected_web_contents_(NULL), | |
209 web_contents_(web_contents), | |
210 browser_(NULL), | |
211 dock_side_(dock_side), | |
212 is_loaded_(false), | |
213 action_on_load_(DEVTOOLS_TOGGLE_ACTION_SHOW), | |
214 width_(-1), | |
215 height_(-1) { | |
216 frontend_host_ = DevToolsClientHost::CreateDevToolsFrontendHost(web_contents, | |
217 this); | |
218 file_helper_.reset(new DevToolsFileHelper(profile, this)); | |
219 | |
220 g_instances.Get().push_back(this); | |
221 // Wipe out page icon so that the default application icon is used. | |
222 NavigationEntry* entry = web_contents->GetController().GetActiveEntry(); | |
223 entry->GetFavicon().image = gfx::Image(); | |
224 entry->GetFavicon().valid = true; | |
225 | |
226 // Register on-load actions. | |
227 registrar_.Add( | |
228 this, | |
229 content::NOTIFICATION_LOAD_STOP, | |
230 content::Source<NavigationController>(&web_contents->GetController())); | |
231 registrar_.Add( | |
232 this, | |
233 chrome::NOTIFICATION_TAB_CLOSING, | |
234 content::Source<NavigationController>(&web_contents->GetController())); | |
235 registrar_.Add( | |
236 this, | |
237 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
238 content::Source<ThemeService>( | |
239 ThemeServiceFactory::GetForProfile(profile_))); | |
240 // There is no inspected_rvh in case of shared workers. | |
241 if (inspected_rvh) | |
242 inspected_web_contents_ = WebContents::FromRenderViewHost(inspected_rvh); | |
243 } | |
244 | |
245 DevToolsWindow::~DevToolsWindow() { | |
246 DevToolsWindowList& instances = g_instances.Get(); | |
247 DevToolsWindowList::iterator it = std::find(instances.begin(), | |
248 instances.end(), | |
249 this); | |
250 DCHECK(it != instances.end()); | |
251 instances.erase(it); | |
252 } | |
253 | |
254 void DevToolsWindow::InspectedContentsClosing() { | |
255 if (IsDocked()) { | |
256 // Update dev tools to reflect removed dev tools window. | |
257 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
258 if (inspected_window) | |
259 inspected_window->UpdateDevTools(); | |
260 // In case of docked web_contents_, we own it so delete here. | |
261 delete web_contents_; | |
262 | |
263 delete this; | |
264 } else { | |
265 // First, initiate self-destruct to free all the registrars. | |
266 // Then close all tabs. Browser will take care of deleting web_contents_ | |
267 // for us. | |
268 Browser* browser = browser_; | |
269 delete this; | |
270 browser->tab_strip_model()->CloseAllTabs(); | |
271 } | |
272 } | |
273 | |
274 void DevToolsWindow::ContentsReplaced(WebContents* new_contents) { | |
275 inspected_web_contents_ = new_contents; | |
276 } | |
277 | |
278 void DevToolsWindow::Show(DevToolsToggleAction action) { | |
279 if (IsDocked()) { | |
280 Browser* inspected_browser; | |
281 int inspected_tab_index; | |
282 // Tell inspected browser to update splitter and switch to inspected panel. | |
283 if (!IsInspectedBrowserPopupOrPanel() && | |
284 FindInspectedBrowserAndTabIndex(&inspected_browser, | |
285 &inspected_tab_index)) { | |
286 BrowserWindow* inspected_window = inspected_browser->window(); | |
287 web_contents_->SetDelegate(this); | |
288 inspected_window->UpdateDevTools(); | |
289 web_contents_->GetView()->SetInitialFocus(); | |
290 inspected_window->Show(); | |
291 TabStripModel* tab_strip_model = inspected_browser->tab_strip_model(); | |
292 tab_strip_model->ActivateTabAt(inspected_tab_index, true); | |
293 ScheduleAction(action); | |
294 return; | |
295 } else { | |
296 // Sometimes we don't know where to dock. Stay undocked. | |
297 dock_side_ = DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
298 } | |
299 } | |
300 | |
301 // Avoid consecutive window switching if the devtools window has been opened | |
302 // and the Inspect Element shortcut is pressed in the inspected tab. | |
303 bool should_show_window = | |
304 !browser_ || action != DEVTOOLS_TOGGLE_ACTION_INSPECT; | |
305 | |
306 if (!browser_) | |
307 CreateDevToolsBrowser(); | |
308 | |
309 if (should_show_window) { | |
310 browser_->window()->Show(); | |
311 web_contents_->GetView()->SetInitialFocus(); | |
312 } | |
313 | |
314 ScheduleAction(action); | |
315 } | |
316 | |
317 int DevToolsWindow::GetWidth(int container_width) { | |
318 if (width_ == -1) { | |
319 width_ = profile_->GetPrefs()-> | |
320 GetInteger(prefs::kDevToolsVSplitLocation); | |
321 } | |
322 | |
323 // By default, size devtools as 1/3 of the browser window. | |
324 if (width_ == -1) | |
325 width_ = container_width / 3; | |
326 | |
327 // Respect the minimum devtools width preset. | |
328 width_ = std::max(kMinDevToolsWidth, width_); | |
329 | |
330 // But it should never compromise the content window size unless the entire | |
331 // window is tiny. | |
332 width_ = std::min(container_width - kMinContentsSize, width_); | |
333 if (width_ < (kMinContentsSize / 2)) | |
334 width_ = container_width / 3; | |
335 return width_; | |
336 } | |
337 | |
338 int DevToolsWindow::GetHeight(int container_height) { | |
339 if (height_ == -1) { | |
340 height_ = profile_->GetPrefs()-> | |
341 GetInteger(prefs::kDevToolsHSplitLocation); | |
342 } | |
343 | |
344 // By default, size devtools as 1/3 of the browser window. | |
345 if (height_ == -1) | |
346 height_ = container_height / 3; | |
347 | |
348 // Respect the minimum devtools width preset. | |
349 height_ = std::max(kMinDevToolsHeight, height_); | |
350 | |
351 // But it should never compromise the content window size. | |
352 height_ = std::min(container_height - kMinContentsSize, height_); | |
353 if (height_ < (kMinContentsSize / 2)) | |
354 height_ = container_height / 3; | |
355 return height_; | |
356 } | |
357 | |
358 void DevToolsWindow::SetWidth(int width) { | |
359 width_ = width; | |
360 profile_->GetPrefs()->SetInteger(prefs::kDevToolsVSplitLocation, width); | |
361 } | |
362 | |
363 void DevToolsWindow::SetHeight(int height) { | |
364 height_ = height; | |
365 profile_->GetPrefs()->SetInteger(prefs::kDevToolsHSplitLocation, height); | |
366 } | |
367 | |
368 RenderViewHost* DevToolsWindow::GetRenderViewHost() { | |
369 return web_contents_->GetRenderViewHost(); | |
370 } | |
371 | |
372 void DevToolsWindow::CreateDevToolsBrowser() { | |
373 // TODO(pfeldman): Make browser's getter for this key static. | |
374 std::string wp_key; | |
375 wp_key.append(prefs::kBrowserWindowPlacement); | |
376 wp_key.append("_"); | |
377 wp_key.append(kDevToolsApp); | |
378 | |
379 PrefService* prefs = profile_->GetPrefs(); | |
380 if (!prefs->FindPreference(wp_key.c_str())) { | |
381 prefs->RegisterDictionaryPref(wp_key.c_str(), PrefService::UNSYNCABLE_PREF); | |
382 } | |
383 | |
384 const DictionaryValue* wp_pref = prefs->GetDictionary(wp_key.c_str()); | |
385 if (!wp_pref || wp_pref->empty()) { | |
386 DictionaryPrefUpdate update(prefs, wp_key.c_str()); | |
387 DictionaryValue* defaults = update.Get(); | |
388 defaults->SetInteger("left", 100); | |
389 defaults->SetInteger("top", 100); | |
390 defaults->SetInteger("right", 740); | |
391 defaults->SetInteger("bottom", 740); | |
392 defaults->SetBoolean("maximized", false); | |
393 defaults->SetBoolean("always_on_top", false); | |
394 } | |
395 | |
396 browser_ = new Browser(Browser::CreateParams::CreateForDevTools(profile_)); | |
397 browser_->tab_strip_model()->AddWebContents( | |
398 web_contents_, -1, content::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
399 TabStripModel::ADD_ACTIVE); | |
400 } | |
401 | |
402 bool DevToolsWindow::FindInspectedBrowserAndTabIndex(Browser** browser, | |
403 int* tab) { | |
404 if (!inspected_web_contents_) | |
405 return false; | |
406 | |
407 bool found = FindInspectedBrowserAndTabIndexFromBrowserList( | |
408 chrome::BrowserListImpl::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | |
409 browser, | |
410 tab); | |
411 // On Windows 8 we can have the desktop environment and the ASH environment | |
412 // active concurrently. If we fail to find the inspected web contents in the | |
413 // native browser list, then we should look in the ASH browser list. | |
414 #if defined(OS_WIN) && defined(USE_AURA) | |
415 if (!found) { | |
416 found = FindInspectedBrowserAndTabIndexFromBrowserList( | |
417 chrome::BrowserListImpl::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), | |
418 browser, | |
419 tab); | |
420 } | |
421 #endif | |
422 return found; | |
423 } | |
424 | |
425 bool DevToolsWindow::FindInspectedBrowserAndTabIndexFromBrowserList( | |
426 chrome::BrowserListImpl* browser_list, | |
427 Browser** browser, | |
428 int* tab) { | |
429 if (!inspected_web_contents_) | |
430 return false; | |
431 | |
432 for (chrome::BrowserListImpl::const_iterator it = browser_list->begin(); | |
433 it != browser_list->end(); ++it) { | |
434 int tab_index = (*it)->tab_strip_model()->GetIndexOfWebContents( | |
435 inspected_web_contents_); | |
436 if (tab_index != TabStripModel::kNoTab) { | |
437 *browser = *it; | |
438 *tab = tab_index; | |
439 return true; | |
440 } | |
441 } | |
442 return false; | |
443 } | |
444 | |
445 BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() { | |
446 Browser* browser = NULL; | |
447 int tab; | |
448 return FindInspectedBrowserAndTabIndex(&browser, &tab) ? | |
449 browser->window() : NULL; | |
450 } | |
451 | |
452 bool DevToolsWindow::IsInspectedBrowserPopupOrPanel() { | |
453 Browser* browser = NULL; | |
454 int tab; | |
455 if (!FindInspectedBrowserAndTabIndex(&browser, &tab)) | |
456 return false; | |
457 | |
458 return browser->is_type_popup() || browser->is_type_panel(); | |
459 } | |
460 | |
461 void DevToolsWindow::UpdateFrontendDockSide() { | |
462 base::StringValue dock_side(SideToString(dock_side_)); | |
463 CallClientFunction("InspectorFrontendAPI.setDockSide", &dock_side); | |
464 base::FundamentalValue docked(IsDocked()); | |
465 CallClientFunction("InspectorFrontendAPI.setAttachedWindow", &docked); | |
466 } | |
467 | |
468 | |
469 void DevToolsWindow::AddDevToolsExtensionsToClient() { | |
470 if (inspected_web_contents_) { | |
471 SessionTabHelper* session_tab_helper = | |
472 SessionTabHelper::FromWebContents(inspected_web_contents_); | |
473 if (session_tab_helper) { | |
474 base::FundamentalValue tabId(session_tab_helper->session_id().id()); | |
475 CallClientFunction("WebInspector.setInspectedTabId", &tabId); | |
476 } | |
477 } | |
478 ListValue results; | |
479 Profile* profile = | |
480 Profile::FromBrowserContext(web_contents_->GetBrowserContext()); | |
481 const ExtensionService* extension_service = extensions::ExtensionSystem::Get( | |
482 profile->GetOriginalProfile())->extension_service(); | |
483 if (!extension_service) | |
484 return; | |
485 | |
486 const ExtensionSet* extensions = extension_service->extensions(); | |
487 | |
488 for (ExtensionSet::const_iterator extension = extensions->begin(); | |
489 extension != extensions->end(); ++extension) { | |
490 if ((*extension)->devtools_url().is_empty()) | |
491 continue; | |
492 DictionaryValue* extension_info = new DictionaryValue(); | |
493 extension_info->Set("startPage", | |
494 new StringValue((*extension)->devtools_url().spec())); | |
495 extension_info->Set("name", new StringValue((*extension)->name())); | |
496 bool allow_experimental = (*extension)->HasAPIPermission( | |
497 extensions::APIPermission::kExperimental); | |
498 extension_info->Set("exposeExperimentalAPIs", | |
499 new base::FundamentalValue(allow_experimental)); | |
500 results.Append(extension_info); | |
501 } | |
502 CallClientFunction("WebInspector.addExtensions", &results); | |
503 } | |
504 | |
505 WebContents* DevToolsWindow::OpenURLFromTab(WebContents* source, | |
506 const OpenURLParams& params) { | |
507 if (inspected_web_contents_) | |
508 return inspected_web_contents_->OpenURL(params); | |
509 return NULL; | |
510 } | |
511 | |
512 void DevToolsWindow::CallClientFunction(const std::string& function_name, | |
513 const Value* arg) { | |
514 std::string json; | |
515 if (arg) | |
516 base::JSONWriter::Write(arg, &json); | |
517 | |
518 string16 javascript = | |
519 ASCIIToUTF16(function_name + "(" + json + ");"); | |
520 web_contents_->GetRenderViewHost()-> | |
521 ExecuteJavascriptInWebFrame(string16(), javascript); | |
522 } | |
523 | |
524 void DevToolsWindow::Observe(int type, | |
525 const content::NotificationSource& source, | |
526 const content::NotificationDetails& details) { | |
527 if (type == content::NOTIFICATION_LOAD_STOP && !is_loaded_) { | |
528 is_loaded_ = true; | |
529 UpdateTheme(); | |
530 DoAction(); | |
531 AddDevToolsExtensionsToClient(); | |
532 } else if (type == chrome::NOTIFICATION_TAB_CLOSING) { | |
533 if (content::Source<NavigationController>(source).ptr() == | |
534 &web_contents_->GetController()) { | |
535 // This happens when browser closes all of its tabs as a result | |
536 // of window.Close event. | |
537 // Notify manager that this DevToolsClientHost no longer exists and | |
538 // initiate self-destuct here. | |
539 DevToolsManager::GetInstance()->ClientHostClosing(frontend_host_); | |
540 UpdateBrowserToolbar(); | |
541 delete this; | |
542 } | |
543 } else if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) { | |
544 UpdateTheme(); | |
545 } | |
546 } | |
547 | |
548 void DevToolsWindow::ScheduleAction(DevToolsToggleAction action) { | |
549 action_on_load_ = action; | |
550 if (is_loaded_) | |
551 DoAction(); | |
552 } | |
553 | |
554 void DevToolsWindow::DoAction() { | |
555 UpdateFrontendDockSide(); | |
556 switch (action_on_load_) { | |
557 case DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE: | |
558 CallClientFunction("InspectorFrontendAPI.showConsole", NULL); | |
559 break; | |
560 case DEVTOOLS_TOGGLE_ACTION_INSPECT: | |
561 CallClientFunction("InspectorFrontendAPI.enterInspectElementMode", NULL); | |
562 case DEVTOOLS_TOGGLE_ACTION_SHOW: | |
563 case DEVTOOLS_TOGGLE_ACTION_TOGGLE: | |
564 // Do nothing. | |
565 break; | |
566 default: | |
567 NOTREACHED(); | |
568 } | |
569 action_on_load_ = DEVTOOLS_TOGGLE_ACTION_SHOW; | |
570 } | |
571 | |
572 std::string SkColorToRGBAString(SkColor color) { | |
573 // We convert the alpha using DoubleToString because StringPrintf will use | |
574 // locale specific formatters (e.g., use , instead of . in German). | |
575 return StringPrintf("rgba(%d,%d,%d,%s)", SkColorGetR(color), | |
576 SkColorGetG(color), SkColorGetB(color), | |
577 base::DoubleToString(SkColorGetA(color) / 255.0).c_str()); | |
578 } | |
579 | |
580 // static | |
581 GURL DevToolsWindow::GetDevToolsUrl(Profile* profile, | |
582 DevToolsDockSide dock_side, | |
583 bool shared_worker_frontend) { | |
584 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile); | |
585 CHECK(tp); | |
586 | |
587 SkColor color_toolbar = | |
588 tp->GetColor(ThemeService::COLOR_TOOLBAR); | |
589 SkColor color_tab_text = | |
590 tp->GetColor(ThemeService::COLOR_BOOKMARK_TEXT); | |
591 | |
592 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
593 bool experiments_enabled = | |
594 command_line.HasSwitch(switches::kEnableDevToolsExperiments); | |
595 | |
596 std::string url_string = StringPrintf("%sdevtools.html?" | |
597 "dockSide=%s&toolbarColor=%s&textColor=%s%s%s", | |
598 chrome::kChromeUIDevToolsURL, | |
599 SideToString(dock_side).c_str(), | |
600 SkColorToRGBAString(color_toolbar).c_str(), | |
601 SkColorToRGBAString(color_tab_text).c_str(), | |
602 shared_worker_frontend ? "&isSharedWorker=true" : "", | |
603 experiments_enabled ? "&experiments=true" : ""); | |
604 return GURL(url_string); | |
605 } | |
606 | |
607 void DevToolsWindow::UpdateTheme() { | |
608 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_); | |
609 CHECK(tp); | |
610 | |
611 SkColor color_toolbar = | |
612 tp->GetColor(ThemeService::COLOR_TOOLBAR); | |
613 SkColor color_tab_text = | |
614 tp->GetColor(ThemeService::COLOR_BOOKMARK_TEXT); | |
615 std::string command = StringPrintf( | |
616 "InspectorFrontendAPI.setToolbarColors(\"%s\", \"%s\")", | |
617 SkColorToRGBAString(color_toolbar).c_str(), | |
618 SkColorToRGBAString(color_tab_text).c_str()); | |
619 web_contents_->GetRenderViewHost()-> | |
620 ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(command)); | |
621 } | |
622 | |
623 void DevToolsWindow::AddNewContents(WebContents* source, | |
624 WebContents* new_contents, | |
625 WindowOpenDisposition disposition, | |
626 const gfx::Rect& initial_pos, | |
627 bool user_gesture, | |
628 bool* was_blocked) { | |
629 if (inspected_web_contents_) { | |
630 inspected_web_contents_->GetDelegate()->AddNewContents( | |
631 source, new_contents, disposition, initial_pos, user_gesture, | |
632 was_blocked); | |
633 } | |
634 } | |
635 | |
636 bool DevToolsWindow::PreHandleKeyboardEvent( | |
637 WebContents* source, | |
638 const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { | |
639 if (IsDocked()) { | |
640 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
641 if (inspected_window) | |
642 return inspected_window->PreHandleKeyboardEvent( | |
643 event, is_keyboard_shortcut); | |
644 } | |
645 return false; | |
646 } | |
647 | |
648 void DevToolsWindow::HandleKeyboardEvent(WebContents* source, | |
649 const NativeWebKeyboardEvent& event) { | |
650 if (IsDocked()) { | |
651 if (event.windowsKeyCode == 0x08) { | |
652 // Do not navigate back in history on Windows (http://crbug.com/74156). | |
653 return; | |
654 } | |
655 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
656 if (inspected_window) | |
657 inspected_window->HandleKeyboardEvent(event); | |
658 } | |
659 } | |
660 | |
661 // static | |
662 DevToolsWindow* DevToolsWindow::ToggleDevToolsWindow( | |
663 RenderViewHost* inspected_rvh, | |
664 bool force_open, | |
665 DevToolsToggleAction action) { | |
666 DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost( | |
667 inspected_rvh); | |
668 DevToolsManager* manager = DevToolsManager::GetInstance(); | |
669 DevToolsClientHost* host = manager->GetDevToolsClientHostFor(agent); | |
670 DevToolsWindow* window = AsDevToolsWindow(host); | |
671 if (host && !window) { | |
672 // Break remote debugging / extension debugging session. | |
673 host->ReplacedWithAnotherClient(); | |
674 manager->UnregisterDevToolsClientHostFor(agent); | |
675 } | |
676 | |
677 bool do_open = force_open; | |
678 if (!window) { | |
679 Profile* profile = Profile::FromBrowserContext( | |
680 inspected_rvh->GetProcess()->GetBrowserContext()); | |
681 DevToolsDockSide dock_side = GetDockSideFromPrefs(profile); | |
682 window = Create(profile, inspected_rvh, dock_side, false); | |
683 manager->RegisterDevToolsClientHostFor(agent, window->frontend_host_); | |
684 do_open = true; | |
685 } | |
686 | |
687 // Update toolbar to reflect DevTools changes. | |
688 window->UpdateBrowserToolbar(); | |
689 | |
690 // If window is docked and visible, we hide it on toggle. If window is | |
691 // undocked, we show (activate) it. | |
692 if (!window->IsDocked() || do_open) | |
693 window->Show(action); | |
694 else | |
695 manager->UnregisterDevToolsClientHostFor(agent); | |
696 | |
697 return window; | |
698 } | |
699 | |
700 // static | |
701 DevToolsWindow* DevToolsWindow::AsDevToolsWindow( | |
702 DevToolsClientHost* client_host) { | |
703 if (!client_host || g_instances == NULL) | |
704 return NULL; | |
705 DevToolsWindowList& instances = g_instances.Get(); | |
706 for (DevToolsWindowList::iterator it = instances.begin(); | |
707 it != instances.end(); ++it) { | |
708 if ((*it)->frontend_host_ == client_host) | |
709 return *it; | |
710 } | |
711 return NULL; | |
712 } | |
713 | |
714 // static | |
715 DevToolsWindow* DevToolsWindow::AsDevToolsWindow(RenderViewHost* window_rvh) { | |
716 if (g_instances == NULL) | |
717 return NULL; | |
718 DevToolsWindowList& instances = g_instances.Get(); | |
719 for (DevToolsWindowList::iterator it = instances.begin(); | |
720 it != instances.end(); ++it) { | |
721 if ((*it)->web_contents_->GetRenderViewHost() == window_rvh) | |
722 return *it; | |
723 } | |
724 return NULL; | |
725 } | |
726 | |
727 void DevToolsWindow::ActivateWindow() { | |
728 if (!IsDocked()) { | |
729 if (!browser_->window()->IsActive()) { | |
730 browser_->window()->Activate(); | |
731 } | |
732 } else { | |
733 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
734 if (inspected_window) | |
735 web_contents_->GetView()->Focus(); | |
736 } | |
737 } | |
738 | |
739 void DevToolsWindow::CloseWindow() { | |
740 DCHECK(IsDocked()); | |
741 DevToolsManager::GetInstance()->ClientHostClosing(frontend_host_); | |
742 InspectedContentsClosing(); | |
743 } | |
744 | |
745 void DevToolsWindow::MoveWindow(int x, int y) { | |
746 if (!IsDocked()) { | |
747 gfx::Rect bounds = browser_->window()->GetBounds(); | |
748 bounds.Offset(x, y); | |
749 browser_->window()->SetBounds(bounds); | |
750 } | |
751 } | |
752 | |
753 void DevToolsWindow::SetDockSide(const std::string& side) { | |
754 DevToolsDockSide requested_side = SideFromString(side); | |
755 bool dock_requested = requested_side != DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
756 bool is_docked = IsDocked(); | |
757 | |
758 if (dock_requested && (!inspected_web_contents_ || | |
759 !GetInspectedBrowserWindow() || IsInspectedBrowserPopupOrPanel())) { | |
760 // Cannot dock, avoid window flashing due to close-reopen cycle. | |
761 return; | |
762 } | |
763 | |
764 dock_side_ = requested_side; | |
765 if (dock_requested) { | |
766 if (!is_docked) { | |
767 // Detach window from the external devtools browser. It will lead to | |
768 // the browser object's close and delete. Remove observer first. | |
769 TabStripModel* tab_strip_model = browser_->tab_strip_model(); | |
770 tab_strip_model->DetachWebContentsAt( | |
771 tab_strip_model->GetIndexOfWebContents(web_contents_)); | |
772 browser_ = NULL; | |
773 } | |
774 } else if (is_docked) { | |
775 // Update inspected window to hide split and reset it. | |
776 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
777 if (inspected_window) | |
778 inspected_window->UpdateDevTools(); | |
779 } | |
780 | |
781 std::string pref_value = kPrefBottom; | |
782 switch (dock_side_) { | |
783 case DEVTOOLS_DOCK_SIDE_UNDOCKED: | |
784 pref_value = kPrefUndocked; | |
785 break; | |
786 case DEVTOOLS_DOCK_SIDE_RIGHT: | |
787 pref_value = kPrefRight; | |
788 break; | |
789 case DEVTOOLS_DOCK_SIDE_BOTTOM: | |
790 pref_value = kPrefBottom; | |
791 break; | |
792 } | |
793 profile_->GetPrefs()->SetString(prefs::kDevToolsDockSide, pref_value); | |
794 | |
795 Show(DEVTOOLS_TOGGLE_ACTION_SHOW); | |
796 } | |
797 | |
798 void DevToolsWindow::OpenInNewTab(const std::string& url) { | |
799 OpenURLParams params(GURL(url), | |
800 content::Referrer(), | |
801 NEW_FOREGROUND_TAB, | |
802 content::PAGE_TRANSITION_LINK, | |
803 false /* is_renderer_initiated */); | |
804 if (inspected_web_contents_) { | |
805 inspected_web_contents_->OpenURL(params); | |
806 } else { | |
807 for (BrowserList::const_iterator it = BrowserList::begin(); | |
808 it != BrowserList::end(); ++it) { | |
809 if ((*it)->type() == Browser::TYPE_TABBED) { | |
810 (*it)->OpenURL(params); | |
811 break; | |
812 } | |
813 } | |
814 } | |
815 } | |
816 | |
817 void DevToolsWindow::SaveToFile(const std::string& url, | |
818 const std::string& content, | |
819 bool save_as) { | |
820 file_helper_->Save(url, content, save_as); | |
821 } | |
822 | |
823 void DevToolsWindow::AppendToFile(const std::string& url, | |
824 const std::string& content) { | |
825 file_helper_->Append(url, content); | |
826 } | |
827 | |
828 void DevToolsWindow::FileSavedAs(const std::string& url) { | |
829 StringValue url_value(url); | |
830 CallClientFunction("InspectorFrontendAPI.savedURL", &url_value); | |
831 } | |
832 | |
833 void DevToolsWindow::AppendedTo(const std::string& url) { | |
834 StringValue url_value(url); | |
835 CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value); | |
836 } | |
837 | |
838 content::JavaScriptDialogCreator* DevToolsWindow::GetJavaScriptDialogCreator() { | |
839 if (inspected_web_contents_ && inspected_web_contents_->GetDelegate()) { | |
840 return inspected_web_contents_->GetDelegate()-> | |
841 GetJavaScriptDialogCreator(); | |
842 } | |
843 return content::WebContentsDelegate::GetJavaScriptDialogCreator(); | |
844 } | |
845 | |
846 void DevToolsWindow::RunFileChooser(WebContents* web_contents, | |
847 const FileChooserParams& params) { | |
848 FileSelectHelper::RunFileChooser(web_contents, params); | |
849 } | |
850 | |
851 void DevToolsWindow::WebContentsFocused(WebContents* contents) { | |
852 Browser* inspected_browser = NULL; | |
853 int inspected_tab_index = -1; | |
854 | |
855 if (IsDocked() && FindInspectedBrowserAndTabIndex(&inspected_browser, | |
856 &inspected_tab_index)) { | |
857 inspected_browser->window()->WebContentsFocused(contents); | |
858 } | |
859 } | |
860 | |
861 void DevToolsWindow::UpdateBrowserToolbar() { | |
862 if (!inspected_web_contents_) | |
863 return; | |
864 BrowserWindow* inspected_window = GetInspectedBrowserWindow(); | |
865 if (inspected_window) | |
866 inspected_window->UpdateToolbar(inspected_web_contents_, false); | |
867 } | |
868 | |
869 bool DevToolsWindow::IsDocked() { | |
870 return dock_side_ != DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
871 } | |
872 | |
873 // static | |
874 DevToolsDockSide DevToolsWindow::GetDockSideFromPrefs(Profile* profile) { | |
875 std::string dock_side = | |
876 profile->GetPrefs()->GetString(prefs::kDevToolsDockSide); | |
877 | |
878 // Migrate prefs | |
879 if (dock_side == kOldPrefBottom || dock_side == kOldPrefRight) { | |
880 bool docked = profile->GetPrefs()->GetBoolean(prefs::kDevToolsOpenDocked); | |
881 if (dock_side == kOldPrefBottom) | |
882 return docked ? DEVTOOLS_DOCK_SIDE_BOTTOM : DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
883 else | |
884 return docked ? DEVTOOLS_DOCK_SIDE_RIGHT : DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
885 } | |
886 | |
887 if (dock_side == kPrefUndocked) | |
888 return DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
889 else if (dock_side == kPrefRight) | |
890 return DEVTOOLS_DOCK_SIDE_RIGHT; | |
891 // Default to docked to bottom | |
892 return DEVTOOLS_DOCK_SIDE_BOTTOM; | |
893 } | |
894 | |
895 // static | |
896 std::string DevToolsWindow::SideToString(DevToolsDockSide dock_side) { | |
897 std::string dock_side_string; | |
898 switch (dock_side) { | |
899 case DEVTOOLS_DOCK_SIDE_UNDOCKED: return kDockSideUndocked; | |
900 case DEVTOOLS_DOCK_SIDE_RIGHT: return kDockSideRight; | |
901 case DEVTOOLS_DOCK_SIDE_BOTTOM: return kDockSideBottom; | |
902 } | |
903 return kDockSideUndocked; | |
904 } | |
905 | |
906 // static | |
907 DevToolsDockSide DevToolsWindow::SideFromString( | |
908 const std::string& dock_side) { | |
909 if (dock_side == kDockSideRight) | |
910 return DEVTOOLS_DOCK_SIDE_RIGHT; | |
911 if (dock_side == kDockSideBottom) | |
912 return DEVTOOLS_DOCK_SIDE_BOTTOM; | |
913 return DEVTOOLS_DOCK_SIDE_UNDOCKED; | |
914 } | |
OLD | NEW |