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