| 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 |