1 // Copyright 2014 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_ui_bindings.h" 6 7 #include "base/command_line.h" 8 #include "base/json/json_reader.h" 9 #include "base/json/json_writer.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/values.h" 15 #include "chrome/browser/chrome_notification_types.h" 16 #include "chrome/browser/chrome_page_zoom.h" 17 #include "chrome/browser/devtools/devtools_target_impl.h" 18 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h" 19 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/browser/infobars/infobar_service.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/themes/theme_properties.h" 23 #include "chrome/browser/themes/theme_service.h" 24 #include "chrome/browser/themes/theme_service_factory.h" 25 #include "chrome/browser/ui/browser.h" 26 #include "chrome/browser/ui/browser_iterator.h" 27 #include "chrome/browser/ui/browser_list.h" 28 #include "chrome/browser/ui/browser_window.h" 29 #include "chrome/browser/ui/tabs/tab_strip_model.h" 30 #include "chrome/common/chrome_switches.h" 31 #include "chrome/common/extensions/manifest_url_handler.h" 32 #include "chrome/common/url_constants.h" 33 #include "components/infobars/core/confirm_infobar_delegate.h" 34 #include "components/infobars/core/infobar.h" 35 #include "content/public/browser/devtools_client_host.h" 36 #include "content/public/browser/devtools_manager.h" 37 #include "content/public/browser/favicon_status.h" 38 #include "content/public/browser/invalidate_type.h" 39 #include "content/public/browser/navigation_controller.h" 40 #include "content/public/browser/navigation_entry.h" 41 #include "content/public/browser/notification_source.h" 42 #include "content/public/browser/render_frame_host.h" 43 #include "content/public/browser/render_view_host.h" 44 #include "content/public/browser/user_metrics.h" 45 #include "content/public/browser/web_contents.h" 46 #include "content/public/browser/web_contents_observer.h" 47 #include "content/public/common/page_transition_types.h" 48 #include "content/public/common/renderer_preferences.h" 49 #include "content/public/common/url_constants.h" 50 #include "extensions/browser/extension_system.h" 51 #include "extensions/common/extension_set.h" 52 #include "extensions/common/permissions/permissions_data.h" 53 #include "grit/generated_resources.h" 54 #include "ui/base/l10n/l10n_util.h" 55 56 using base::DictionaryValue; 57 using content::BrowserThread; 58 59 namespace { 60 61 static const char kFrontendHostId[] = "id"; 62 static const char kFrontendHostMethod[] = "method"; 63 static const char kFrontendHostParams[] = "params"; 64 static const char kTitleFormat[] = "Developer Tools - %s"; 65 66 static const char kDevicesChanged[] = "DevicesChanged"; 67 static const char kDeviceCountChanged[] = "DeviceCountChanged"; 68 69 std::string SkColorToRGBAString(SkColor color) { 70 // We avoid StringPrintf because it will use locale specific formatters for 71 // the double (e.g. ',' instead of '.' in German). 72 return "rgba(" + base::IntToString(SkColorGetR(color)) + "," + 73 base::IntToString(SkColorGetG(color)) + "," + 74 base::IntToString(SkColorGetB(color)) + "," + 75 base::DoubleToString(SkColorGetA(color) / 255.0) + ")"; 76 } 77 78 base::DictionaryValue* CreateFileSystemValue( 79 DevToolsFileHelper::FileSystem file_system) { 80 base::DictionaryValue* file_system_value = new base::DictionaryValue(); 81 file_system_value->SetString("fileSystemName", file_system.file_system_name); 82 file_system_value->SetString("rootURL", file_system.root_url); 83 file_system_value->SetString("fileSystemPath", file_system.file_system_path); 84 return file_system_value; 85 } 86 87 Browser* FindBrowser(content::WebContents* web_contents) { 88 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 89 int tab_index = it->tab_strip_model()->GetIndexOfWebContents( 90 web_contents); 91 if (tab_index != TabStripModel::kNoTab) 92 return *it; 93 } 94 return NULL; 95 } 96 97 // DevToolsConfirmInfoBarDelegate --------------------------------------------- 98 99 typedef base::Callback<void(bool)> InfoBarCallback; 100 101 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate { 102 public: 103 // If |infobar_service| is NULL, runs |callback| with a single argument with 104 // value "false". Otherwise, creates a dev tools confirm infobar and delegate 105 // and adds the infobar to |infobar_service|. 106 static void Create(InfoBarService* infobar_service, 107 const InfoBarCallback& callback, 108 const base::string16& message); 109 110 private: 111 DevToolsConfirmInfoBarDelegate( 112 const InfoBarCallback& callback, 113 const base::string16& message); 114 virtual ~DevToolsConfirmInfoBarDelegate(); 115 116 virtual base::string16 GetMessageText() const OVERRIDE; 117 virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; 118 virtual bool Accept() OVERRIDE; 119 virtual bool Cancel() OVERRIDE; 120 121 InfoBarCallback callback_; 122 const base::string16 message_; 123 124 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate); 125 }; 126 127 void DevToolsConfirmInfoBarDelegate::Create( 128 InfoBarService* infobar_service, 129 const InfoBarCallback& callback, 130 const base::string16& message) { 131 if (!infobar_service) { 132 callback.Run(false); 133 return; 134 } 135 136 infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 137 scoped_ptr<ConfirmInfoBarDelegate>( 138 new DevToolsConfirmInfoBarDelegate(callback, message)))); 139 } 140 141 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate( 142 const InfoBarCallback& callback, 143 const base::string16& message) 144 : ConfirmInfoBarDelegate(), 145 callback_(callback), 146 message_(message) { 147 } 148 149 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() { 150 if (!callback_.is_null()) 151 callback_.Run(false); 152 } 153 154 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const { 155 return message_; 156 } 157 158 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel( 159 InfoBarButton button) const { 160 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 161 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON); 162 } 163 164 bool DevToolsConfirmInfoBarDelegate::Accept() { 165 callback_.Run(true); 166 callback_.Reset(); 167 return true; 168 } 169 170 bool DevToolsConfirmInfoBarDelegate::Cancel() { 171 callback_.Run(false); 172 callback_.Reset(); 173 return true; 174 } 175 176 // DevToolsUIDefaultDelegate -------------------------------------------------- 177 178 class DefaultBindingsDelegate : public DevToolsUIBindings::Delegate { 179 public: 180 explicit DefaultBindingsDelegate(content::WebContents* web_contents) 181 : web_contents_(web_contents) {} 182 183 private: 184 virtual ~DefaultBindingsDelegate() {} 185 186 virtual void ActivateWindow() OVERRIDE; 187 virtual void CloseWindow() OVERRIDE {} 188 virtual void SetInspectedPageBounds(const gfx::Rect& rect) OVERRIDE {} 189 virtual void SetContentsResizingStrategy( 190 const gfx::Insets& insets, const gfx::Size& min_size) OVERRIDE {} 191 virtual void InspectElementCompleted() OVERRIDE {} 192 virtual void MoveWindow(int x, int y) OVERRIDE {} 193 virtual void SetIsDocked(bool is_docked) OVERRIDE {} 194 virtual void OpenInNewTab(const std::string& url) OVERRIDE; 195 virtual void SetWhitelistedShortcuts(const std::string& message) OVERRIDE {} 196 197 virtual void InspectedContentsClosing() OVERRIDE; 198 virtual void OnLoadCompleted() OVERRIDE {} 199 virtual InfoBarService* GetInfoBarService() OVERRIDE; 200 virtual void RenderProcessGone() OVERRIDE {} 201 202 content::WebContents* web_contents_; 203 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate); 204 }; 205 206 void DefaultBindingsDelegate::ActivateWindow() { 207 web_contents_->GetDelegate()->ActivateContents(web_contents_); 208 web_contents_->Focus(); 209 } 210 211 void DefaultBindingsDelegate::OpenInNewTab(const std::string& url) { 212 content::OpenURLParams params( 213 GURL(url), content::Referrer(), NEW_FOREGROUND_TAB, 214 content::PAGE_TRANSITION_LINK, false); 215 Browser* browser = FindBrowser(web_contents_); 216 browser->OpenURL(params); 217 } 218 219 void DefaultBindingsDelegate::InspectedContentsClosing() { 220 web_contents_->GetRenderViewHost()->ClosePage(); 221 } 222 223 InfoBarService* DefaultBindingsDelegate::GetInfoBarService() { 224 return InfoBarService::FromWebContents(web_contents_); 225 } 226 227 } // namespace 228 229 // DevToolsUIBindings::FrontendWebContentsObserver ---------------------------- 230 231 class DevToolsUIBindings::FrontendWebContentsObserver 232 : public content::WebContentsObserver { 233 public: 234 explicit FrontendWebContentsObserver(DevToolsUIBindings* ui_bindings); 235 virtual ~FrontendWebContentsObserver(); 236 237 private: 238 // contents::WebContentsObserver: 239 virtual void WebContentsDestroyed() OVERRIDE; 240 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; 241 virtual void AboutToNavigateRenderView( 242 content::RenderViewHost* render_view_host) OVERRIDE; 243 virtual void DocumentOnLoadCompletedInMainFrame() OVERRIDE; 244 245 DevToolsUIBindings* devtools_bindings_; 246 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver); 247 }; 248 249 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver( 250 DevToolsUIBindings* devtools_ui_bindings) 251 : WebContentsObserver(devtools_ui_bindings->web_contents()), 252 devtools_bindings_(devtools_ui_bindings) { 253 } 254 255 DevToolsUIBindings::FrontendWebContentsObserver:: 256 ~FrontendWebContentsObserver() { 257 } 258 259 void DevToolsUIBindings::FrontendWebContentsObserver::WebContentsDestroyed() { 260 delete devtools_bindings_; 261 } 262 263 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone( 264 base::TerminationStatus status) { 265 devtools_bindings_->delegate_->RenderProcessGone(); 266 } 267 268 void DevToolsUIBindings::FrontendWebContentsObserver::AboutToNavigateRenderView( 269 content::RenderViewHost* render_view_host) { 270 content::NavigationEntry* entry = 271 web_contents()->GetController().GetActiveEntry(); 272 if (devtools_bindings_->url_ == entry->GetURL()) 273 content::DevToolsClientHost::SetupDevToolsFrontendClient(render_view_host); 274 else 275 delete devtools_bindings_; 276 } 277 278 void DevToolsUIBindings::FrontendWebContentsObserver:: 279 DocumentOnLoadCompletedInMainFrame() { 280 devtools_bindings_->DocumentOnLoadCompletedInMainFrame(); 281 } 282 283 // DevToolsUIBindings --------------------------------------------------------- 284 285 // static 286 GURL DevToolsUIBindings::ApplyThemeToURL(Profile* profile, 287 const GURL& base_url) { 288 std::string frontend_url = base_url.spec(); 289 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile); 290 DCHECK(tp); 291 std::string url_string( 292 frontend_url + 293 ((frontend_url.find("?") == std::string::npos) ? "?" : "&") + 294 "dockSide=undocked" + // TODO(dgozman): remove this support in M38. 295 "&toolbarColor=" + 296 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) + 297 "&textColor=" + 298 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT))); 299 if (CommandLine::ForCurrentProcess()->HasSwitch( 300 switches::kEnableDevToolsExperiments)) 301 url_string += "&experiments=true"; 302 return GURL(url_string); 303 } 304 305 DevToolsUIBindings::DevToolsUIBindings(content::WebContents* web_contents, 306 const GURL& url) 307 : profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), 308 web_contents_(web_contents), 309 delegate_(new DefaultBindingsDelegate(web_contents_)), 310 device_listener_enabled_(false), 311 url_(url), 312 weak_factory_(this) { 313 frontend_contents_observer_.reset(new FrontendWebContentsObserver(this)); 314 web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false; 315 316 frontend_host_.reset(content::DevToolsClientHost::CreateDevToolsFrontendHost( 317 web_contents_, this)); 318 file_helper_.reset(new DevToolsFileHelper(web_contents_, profile_)); 319 file_system_indexer_ = new DevToolsFileSystemIndexer(); 320 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents( 321 web_contents_); 322 323 web_contents_->GetController().LoadURL( 324 url, content::Referrer(), 325 content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); 326 327 // Wipe out page icon so that the default application icon is used. 328 content::NavigationEntry* entry = 329 web_contents_->GetController().GetActiveEntry(); 330 entry->GetFavicon().image = gfx::Image(); 331 entry->GetFavicon().valid = true; 332 333 // Register on-load actions. 334 registrar_.Add( 335 this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, 336 content::Source<ThemeService>( 337 ThemeServiceFactory::GetForProfile(profile_))); 338 339 embedder_message_dispatcher_.reset( 340 DevToolsEmbedderMessageDispatcher::createForDevToolsFrontend(this)); 341 } 342 343 DevToolsUIBindings::~DevToolsUIBindings() { 344 content::DevToolsManager::GetInstance()->ClientHostClosing( 345 frontend_host_.get()); 346 347 for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin()); 348 jobs_it != indexing_jobs_.end(); ++jobs_it) { 349 jobs_it->second->Stop(); 350 } 351 indexing_jobs_.clear(); 352 353 while (!subscribers_.empty()) 354 Unsubscribe(*subscribers_.begin()); 355 } 356 357 void DevToolsUIBindings::InspectedContentsClosing() { 358 delegate_->InspectedContentsClosing(); 359 } 360 361 void DevToolsUIBindings::Observe(int type, 362 const content::NotificationSource& source, 363 const content::NotificationDetails& details) { 364 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type); 365 UpdateTheme(); 366 } 367 368 void DevToolsUIBindings::DispatchOnEmbedder(const std::string& message) { 369 std::string method; 370 base::ListValue empty_params; 371 base::ListValue* params = &empty_params; 372 373 base::DictionaryValue* dict = NULL; 374 scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message)); 375 if (!parsed_message || 376 !parsed_message->GetAsDictionary(&dict) || 377 !dict->GetString(kFrontendHostMethod, &method) || 378 (dict->HasKey(kFrontendHostParams) && 379 !dict->GetList(kFrontendHostParams, ¶ms))) { 380 LOG(ERROR) << "Invalid message was sent to embedder: " << message; 381 return; 382 } 383 384 int id = 0; 385 dict->GetInteger(kFrontendHostId, &id); 386 387 std::string error; 388 embedder_message_dispatcher_->Dispatch(method, params, &error); 389 if (id) { 390 scoped_ptr<base::Value> id_value(base::Value::CreateIntegerValue(id)); 391 scoped_ptr<base::Value> error_value(base::Value::CreateStringValue(error)); 392 CallClientFunction("InspectorFrontendAPI.embedderMessageAck", 393 id_value.get(), error_value.get(), NULL); 394 } 395 } 396 397 void DevToolsUIBindings::ActivateWindow() { 398 delegate_->ActivateWindow(); 399 } 400 401 void DevToolsUIBindings::CloseWindow() { 402 delegate_->CloseWindow(); 403 } 404 405 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) { 406 delegate_->SetInspectedPageBounds(rect); 407 } 408 409 void DevToolsUIBindings::SetContentsResizingStrategy( 410 const gfx::Insets& insets, const gfx::Size& min_size) { 411 delegate_->SetContentsResizingStrategy(insets, min_size); 412 } 413 414 void DevToolsUIBindings::MoveWindow(int x, int y) { 415 delegate_->MoveWindow(x, y); 416 } 417 418 void DevToolsUIBindings::SetIsDocked(bool dock_requested) { 419 delegate_->SetIsDocked(dock_requested); 420 } 421 422 void DevToolsUIBindings::InspectElementCompleted() { 423 delegate_->InspectElementCompleted(); 424 } 425 426 void DevToolsUIBindings::InspectedURLChanged(const std::string& url) { 427 content::NavigationController& controller = web_contents()->GetController(); 428 content::NavigationEntry* entry = controller.GetActiveEntry(); 429 // DevTools UI is not localized. 430 entry->SetTitle( 431 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str()))); 432 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE); 433 } 434 435 void DevToolsUIBindings::OpenInNewTab(const std::string& url) { 436 delegate_->OpenInNewTab(url); 437 } 438 439 void DevToolsUIBindings::SaveToFile(const std::string& url, 440 const std::string& content, 441 bool save_as) { 442 file_helper_->Save(url, content, save_as, 443 base::Bind(&DevToolsUIBindings::FileSavedAs, 444 weak_factory_.GetWeakPtr(), url), 445 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs, 446 weak_factory_.GetWeakPtr(), url)); 447 } 448 449 void DevToolsUIBindings::AppendToFile(const std::string& url, 450 const std::string& content) { 451 file_helper_->Append(url, content, 452 base::Bind(&DevToolsUIBindings::AppendedTo, 453 weak_factory_.GetWeakPtr(), url)); 454 } 455 456 void DevToolsUIBindings::RequestFileSystems() { 457 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 458 file_helper_->RequestFileSystems(base::Bind( 459 &DevToolsUIBindings::FileSystemsLoaded, weak_factory_.GetWeakPtr())); 460 } 461 462 void DevToolsUIBindings::AddFileSystem() { 463 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 464 file_helper_->AddFileSystem( 465 base::Bind(&DevToolsUIBindings::FileSystemAdded, 466 weak_factory_.GetWeakPtr()), 467 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar, 468 weak_factory_.GetWeakPtr())); 469 } 470 471 void DevToolsUIBindings::RemoveFileSystem( 472 const std::string& file_system_path) { 473 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 474 file_helper_->RemoveFileSystem(file_system_path); 475 base::StringValue file_system_path_value(file_system_path); 476 CallClientFunction("InspectorFrontendAPI.fileSystemRemoved", 477 &file_system_path_value, NULL, NULL); 478 } 479 480 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions( 481 const std::string& file_system_url) { 482 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 483 file_helper_->UpgradeDraggedFileSystemPermissions( 484 file_system_url, 485 base::Bind(&DevToolsUIBindings::FileSystemAdded, 486 weak_factory_.GetWeakPtr()), 487 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar, 488 weak_factory_.GetWeakPtr())); 489 } 490 491 void DevToolsUIBindings::IndexPath(int request_id, 492 const std::string& file_system_path) { 493 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 494 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 495 if (!file_helper_->IsFileSystemAdded(file_system_path)) { 496 IndexingDone(request_id, file_system_path); 497 return; 498 } 499 indexing_jobs_[request_id] = 500 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>( 501 file_system_indexer_->IndexPath( 502 file_system_path, 503 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated, 504 weak_factory_.GetWeakPtr(), 505 request_id, 506 file_system_path), 507 Bind(&DevToolsUIBindings::IndexingWorked, 508 weak_factory_.GetWeakPtr(), 509 request_id, 510 file_system_path), 511 Bind(&DevToolsUIBindings::IndexingDone, 512 weak_factory_.GetWeakPtr(), 513 request_id, 514 file_system_path))); 515 } 516 517 void DevToolsUIBindings::StopIndexing(int request_id) { 518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 519 IndexingJobsMap::iterator it = indexing_jobs_.find(request_id); 520 if (it == indexing_jobs_.end()) 521 return; 522 it->second->Stop(); 523 indexing_jobs_.erase(it); 524 } 525 526 void DevToolsUIBindings::SearchInPath(int request_id, 527 const std::string& file_system_path, 528 const std::string& query) { 529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 530 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme)); 531 if (!file_helper_->IsFileSystemAdded(file_system_path)) { 532 SearchCompleted(request_id, file_system_path, std::vector<std::string>()); 533 return; 534 } 535 file_system_indexer_->SearchInPath(file_system_path, 536 query, 537 Bind(&DevToolsUIBindings::SearchCompleted, 538 weak_factory_.GetWeakPtr(), 539 request_id, 540 file_system_path)); 541 } 542 543 void DevToolsUIBindings::SetWhitelistedShortcuts( 544 const std::string& message) { 545 delegate_->SetWhitelistedShortcuts(message); 546 } 547 548 void DevToolsUIBindings::ZoomIn() { 549 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_IN); 550 } 551 552 void DevToolsUIBindings::ZoomOut() { 553 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT); 554 } 555 556 void DevToolsUIBindings::ResetZoom() { 557 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET); 558 } 559 560 static void InspectTarget(Profile* profile, DevToolsTargetImpl* target) { 561 if (target) 562 target->Inspect(profile); 563 } 564 565 void DevToolsUIBindings::OpenUrlOnRemoteDeviceAndInspect( 566 const std::string& browser_id, 567 const std::string& url) { 568 if (remote_targets_handler_) { 569 remote_targets_handler_->Open(browser_id, url, 570 base::Bind(&InspectTarget, profile_)); 571 } 572 } 573 574 void DevToolsUIBindings::Subscribe(const std::string& event_type) { 575 if (subscribers_.find(event_type) != subscribers_.end()) { 576 LOG(ERROR) << "Already subscribed for [" << event_type << "]."; 577 return; 578 } 579 580 subscribers_.insert(event_type); 581 582 if (event_type == kDevicesChanged) { 583 remote_targets_handler_ = DevToolsTargetsUIHandler::CreateForAdb( 584 base::Bind(&DevToolsUIBindings::PopulateRemoteDevices, 585 base::Unretained(this)), 586 profile_); 587 } else if (event_type == kDeviceCountChanged) { 588 EnableRemoteDeviceCounter(true); 589 } else { 590 LOG(ERROR) << "Attempt to start unknown event listener " << event_type; 591 } 592 } 593 594 void DevToolsUIBindings::Unsubscribe(const std::string& event_type) { 595 if (subscribers_.find(event_type) == subscribers_.end()) { 596 LOG(ERROR) << "Not yet subscribed for [" << event_type << "]"; 597 return; 598 } 599 600 if (event_type == kDevicesChanged) { 601 remote_targets_handler_.reset(); 602 } else if (event_type == kDeviceCountChanged) { 603 EnableRemoteDeviceCounter(false); 604 } else { 605 LOG(ERROR) << "Attempt to stop unknown event listener " << event_type; 606 } 607 608 subscribers_.erase(event_type); 609 } 610 611 void DevToolsUIBindings::EnableRemoteDeviceCounter(bool enable) { 612 DevToolsAndroidBridge* adb_bridge = 613 DevToolsAndroidBridge::Factory::GetForProfile(profile_); 614 if (!adb_bridge) 615 return; 616 617 DCHECK(device_listener_enabled_ != enable); 618 device_listener_enabled_ = enable; 619 if (enable) 620 adb_bridge->AddDeviceCountListener(this); 621 else 622 adb_bridge->RemoveDeviceCountListener(this); 623 } 624 625 void DevToolsUIBindings::DeviceCountChanged(int count) { 626 base::FundamentalValue value(count); 627 DispatchEventOnFrontend(kDeviceCountChanged, &value); 628 } 629 630 void DevToolsUIBindings::PopulateRemoteDevices( 631 const std::string& source, 632 scoped_ptr<base::ListValue> targets) { 633 DispatchEventOnFrontend(kDevicesChanged, targets.get()); 634 } 635 636 void DevToolsUIBindings::FileSavedAs(const std::string& url) { 637 base::StringValue url_value(url); 638 CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL); 639 } 640 641 void DevToolsUIBindings::CanceledFileSaveAs(const std::string& url) { 642 base::StringValue url_value(url); 643 CallClientFunction("InspectorFrontendAPI.canceledSaveURL", 644 &url_value, NULL, NULL); 645 } 646 647 void DevToolsUIBindings::AppendedTo(const std::string& url) { 648 base::StringValue url_value(url); 649 CallClientFunction("InspectorFrontendAPI.appendedToURL", &url_value, NULL, 650 NULL); 651 } 652 653 void DevToolsUIBindings::FileSystemsLoaded( 654 const std::vector<DevToolsFileHelper::FileSystem>& file_systems) { 655 base::ListValue file_systems_value; 656 for (size_t i = 0; i < file_systems.size(); ++i) 657 file_systems_value.Append(CreateFileSystemValue(file_systems[i])); 658 CallClientFunction("InspectorFrontendAPI.fileSystemsLoaded", 659 &file_systems_value, NULL, NULL); 660 } 661 662 void DevToolsUIBindings::FileSystemAdded( 663 const DevToolsFileHelper::FileSystem& file_system) { 664 scoped_ptr<base::StringValue> error_string_value( 665 new base::StringValue(std::string())); 666 scoped_ptr<base::DictionaryValue> file_system_value; 667 if (!file_system.file_system_path.empty()) 668 file_system_value.reset(CreateFileSystemValue(file_system)); 669 CallClientFunction("InspectorFrontendAPI.fileSystemAdded", 670 error_string_value.get(), file_system_value.get(), NULL); 671 } 672 673 void DevToolsUIBindings::IndexingTotalWorkCalculated( 674 int request_id, 675 const std::string& file_system_path, 676 int total_work) { 677 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 678 base::FundamentalValue request_id_value(request_id); 679 base::StringValue file_system_path_value(file_system_path); 680 base::FundamentalValue total_work_value(total_work); 681 CallClientFunction("InspectorFrontendAPI.indexingTotalWorkCalculated", 682 &request_id_value, &file_system_path_value, 683 &total_work_value); 684 } 685 686 void DevToolsUIBindings::IndexingWorked(int request_id, 687 const std::string& file_system_path, 688 int worked) { 689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 690 base::FundamentalValue request_id_value(request_id); 691 base::StringValue file_system_path_value(file_system_path); 692 base::FundamentalValue worked_value(worked); 693 CallClientFunction("InspectorFrontendAPI.indexingWorked", &request_id_value, 694 &file_system_path_value, &worked_value); 695 } 696 697 void DevToolsUIBindings::IndexingDone(int request_id, 698 const std::string& file_system_path) { 699 indexing_jobs_.erase(request_id); 700 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 701 base::FundamentalValue request_id_value(request_id); 702 base::StringValue file_system_path_value(file_system_path); 703 CallClientFunction("InspectorFrontendAPI.indexingDone", &request_id_value, 704 &file_system_path_value, NULL); 705 } 706 707 void DevToolsUIBindings::SearchCompleted( 708 int request_id, 709 const std::string& file_system_path, 710 const std::vector<std::string>& file_paths) { 711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 712 base::ListValue file_paths_value; 713 for (std::vector<std::string>::const_iterator it(file_paths.begin()); 714 it != file_paths.end(); ++it) { 715 file_paths_value.AppendString(*it); 716 } 717 base::FundamentalValue request_id_value(request_id); 718 base::StringValue file_system_path_value(file_system_path); 719 CallClientFunction("InspectorFrontendAPI.searchCompleted", &request_id_value, 720 &file_system_path_value, &file_paths_value); 721 } 722 723 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar( 724 const base::string16& message, 725 const InfoBarCallback& callback) { 726 DevToolsConfirmInfoBarDelegate::Create(delegate_->GetInfoBarService(), 727 callback, message); 728 } 729 730 void DevToolsUIBindings::UpdateTheme() { 731 ThemeService* tp = ThemeServiceFactory::GetForProfile(profile_); 732 DCHECK(tp); 733 734 std::string command("InspectorFrontendAPI.setToolbarColors(\"" + 735 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_TOOLBAR)) + 736 "\", \"" + 737 SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)) + 738 "\")"); 739 web_contents_->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(command)); 740 } 741 742 void DevToolsUIBindings::AddDevToolsExtensionsToClient() { 743 const ExtensionService* extension_service = extensions::ExtensionSystem::Get( 744 profile_->GetOriginalProfile())->extension_service(); 745 if (!extension_service) 746 return; 747 const extensions::ExtensionSet* extensions = extension_service->extensions(); 748 749 base::ListValue results; 750 for (extensions::ExtensionSet::const_iterator extension(extensions->begin()); 751 extension != extensions->end(); ++extension) { 752 if (extensions::ManifestURL::GetDevToolsPage(extension->get()).is_empty()) 753 continue; 754 base::DictionaryValue* extension_info = new base::DictionaryValue(); 755 extension_info->Set( 756 "startPage", 757 new base::StringValue( 758 extensions::ManifestURL::GetDevToolsPage( 759 extension->get()).spec())); 760 extension_info->Set("name", new base::StringValue((*extension)->name())); 761 extension_info->Set("exposeExperimentalAPIs", 762 new base::FundamentalValue( 763 (*extension)->permissions_data()->HasAPIPermission( 764 extensions::APIPermission::kExperimental))); 765 results.Append(extension_info); 766 } 767 CallClientFunction("WebInspector.addExtensions", &results, NULL, NULL); 768 } 769 770 void DevToolsUIBindings::SetDelegate(Delegate* delegate) { 771 delegate_.reset(delegate); 772 } 773 774 void DevToolsUIBindings::CallClientFunction(const std::string& function_name, 775 const base::Value* arg1, 776 const base::Value* arg2, 777 const base::Value* arg3) { 778 std::string params; 779 if (arg1) { 780 std::string json; 781 base::JSONWriter::Write(arg1, &json); 782 params.append(json); 783 if (arg2) { 784 base::JSONWriter::Write(arg2, &json); 785 params.append(", " + json); 786 if (arg3) { 787 base::JSONWriter::Write(arg3, &json); 788 params.append(", " + json); 789 } 790 } 791 } 792 base::string16 javascript = 793 base::UTF8ToUTF16(function_name + "(" + params + ");"); 794 web_contents_->GetMainFrame()->ExecuteJavaScript(javascript); 795 } 796 797 void DevToolsUIBindings::DispatchEventOnFrontend( 798 const std::string& event_type, 799 const base::Value* event_data) { 800 if (subscribers_.find(event_type) == subscribers_.end()) 801 return; 802 base::StringValue event_type_value = base::StringValue(event_type); 803 CallClientFunction("InspectorFrontendAPI.dispatchEventToListeners", 804 &event_type_value, 805 event_data, 806 NULL); 807 } 808 809 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() { 810 // Call delegate first - it seeds importants bit of information. 811 delegate_->OnLoadCompleted(); 812 813 UpdateTheme(); 814 AddDevToolsExtensionsToClient(); 815 } 816