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
SkColorToRGBAString(SkColor color)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
CreateFileSystemValue(DevToolsFileHelper::FileSystem file_system)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
FindBrowser(content::WebContents * web_contents)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
Create(InfoBarService * infobar_service,const InfoBarCallback & callback,const base::string16 & message)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
DevToolsConfirmInfoBarDelegate(const InfoBarCallback & callback,const base::string16 & message)141 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
142 const InfoBarCallback& callback,
143 const base::string16& message)
144 : ConfirmInfoBarDelegate(),
145 callback_(callback),
146 message_(message) {
147 }
148
~DevToolsConfirmInfoBarDelegate()149 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
150 if (!callback_.is_null())
151 callback_.Run(false);
152 }
153
GetMessageText() const154 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
155 return message_;
156 }
157
GetButtonLabel(InfoBarButton button) const158 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
Accept()164 bool DevToolsConfirmInfoBarDelegate::Accept() {
165 callback_.Run(true);
166 callback_.Reset();
167 return true;
168 }
169
Cancel()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:
DefaultBindingsDelegate(content::WebContents * web_contents)180 explicit DefaultBindingsDelegate(content::WebContents* web_contents)
181 : web_contents_(web_contents) {}
182
183 private:
~DefaultBindingsDelegate()184 virtual ~DefaultBindingsDelegate() {}
185
186 virtual void ActivateWindow() OVERRIDE;
CloseWindow()187 virtual void CloseWindow() OVERRIDE {}
SetInspectedPageBounds(const gfx::Rect & rect)188 virtual void SetInspectedPageBounds(const gfx::Rect& rect) OVERRIDE {}
SetContentsResizingStrategy(const gfx::Insets & insets,const gfx::Size & min_size)189 virtual void SetContentsResizingStrategy(
190 const gfx::Insets& insets, const gfx::Size& min_size) OVERRIDE {}
InspectElementCompleted()191 virtual void InspectElementCompleted() OVERRIDE {}
MoveWindow(int x,int y)192 virtual void MoveWindow(int x, int y) OVERRIDE {}
SetIsDocked(bool is_docked)193 virtual void SetIsDocked(bool is_docked) OVERRIDE {}
194 virtual void OpenInNewTab(const std::string& url) OVERRIDE;
SetWhitelistedShortcuts(const std::string & message)195 virtual void SetWhitelistedShortcuts(const std::string& message) OVERRIDE {}
196
197 virtual void InspectedContentsClosing() OVERRIDE;
OnLoadCompleted()198 virtual void OnLoadCompleted() OVERRIDE {}
199 virtual InfoBarService* GetInfoBarService() OVERRIDE;
RenderProcessGone()200 virtual void RenderProcessGone() OVERRIDE {}
201
202 content::WebContents* web_contents_;
203 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
204 };
205
ActivateWindow()206 void DefaultBindingsDelegate::ActivateWindow() {
207 web_contents_->GetDelegate()->ActivateContents(web_contents_);
208 web_contents_->Focus();
209 }
210
OpenInNewTab(const std::string & url)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
InspectedContentsClosing()219 void DefaultBindingsDelegate::InspectedContentsClosing() {
220 web_contents_->GetRenderViewHost()->ClosePage();
221 }
222
GetInfoBarService()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
FrontendWebContentsObserver(DevToolsUIBindings * devtools_ui_bindings)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::
~FrontendWebContentsObserver()256 ~FrontendWebContentsObserver() {
257 }
258
WebContentsDestroyed()259 void DevToolsUIBindings::FrontendWebContentsObserver::WebContentsDestroyed() {
260 delete devtools_bindings_;
261 }
262
RenderProcessGone(base::TerminationStatus status)263 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
264 base::TerminationStatus status) {
265 devtools_bindings_->delegate_->RenderProcessGone();
266 }
267
AboutToNavigateRenderView(content::RenderViewHost * render_view_host)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::
DocumentOnLoadCompletedInMainFrame()279 DocumentOnLoadCompletedInMainFrame() {
280 devtools_bindings_->DocumentOnLoadCompletedInMainFrame();
281 }
282
283 // DevToolsUIBindings ---------------------------------------------------------
284
285 // static
ApplyThemeToURL(Profile * profile,const GURL & base_url)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
DevToolsUIBindings(content::WebContents * web_contents,const GURL & url)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
~DevToolsUIBindings()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
InspectedContentsClosing()357 void DevToolsUIBindings::InspectedContentsClosing() {
358 delegate_->InspectedContentsClosing();
359 }
360
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
DispatchOnEmbedder(const std::string & message)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
ActivateWindow()397 void DevToolsUIBindings::ActivateWindow() {
398 delegate_->ActivateWindow();
399 }
400
CloseWindow()401 void DevToolsUIBindings::CloseWindow() {
402 delegate_->CloseWindow();
403 }
404
SetInspectedPageBounds(const gfx::Rect & rect)405 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) {
406 delegate_->SetInspectedPageBounds(rect);
407 }
408
SetContentsResizingStrategy(const gfx::Insets & insets,const gfx::Size & min_size)409 void DevToolsUIBindings::SetContentsResizingStrategy(
410 const gfx::Insets& insets, const gfx::Size& min_size) {
411 delegate_->SetContentsResizingStrategy(insets, min_size);
412 }
413
MoveWindow(int x,int y)414 void DevToolsUIBindings::MoveWindow(int x, int y) {
415 delegate_->MoveWindow(x, y);
416 }
417
SetIsDocked(bool dock_requested)418 void DevToolsUIBindings::SetIsDocked(bool dock_requested) {
419 delegate_->SetIsDocked(dock_requested);
420 }
421
InspectElementCompleted()422 void DevToolsUIBindings::InspectElementCompleted() {
423 delegate_->InspectElementCompleted();
424 }
425
InspectedURLChanged(const std::string & url)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
OpenInNewTab(const std::string & url)435 void DevToolsUIBindings::OpenInNewTab(const std::string& url) {
436 delegate_->OpenInNewTab(url);
437 }
438
SaveToFile(const std::string & url,const std::string & content,bool save_as)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
AppendToFile(const std::string & url,const std::string & content)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
RequestFileSystems()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
AddFileSystem()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
RemoveFileSystem(const std::string & file_system_path)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
UpgradeDraggedFileSystemPermissions(const std::string & file_system_url)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
IndexPath(int request_id,const std::string & file_system_path)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
StopIndexing(int request_id)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
SearchInPath(int request_id,const std::string & file_system_path,const std::string & query)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
SetWhitelistedShortcuts(const std::string & message)543 void DevToolsUIBindings::SetWhitelistedShortcuts(
544 const std::string& message) {
545 delegate_->SetWhitelistedShortcuts(message);
546 }
547
ZoomIn()548 void DevToolsUIBindings::ZoomIn() {
549 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_IN);
550 }
551
ZoomOut()552 void DevToolsUIBindings::ZoomOut() {
553 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT);
554 }
555
ResetZoom()556 void DevToolsUIBindings::ResetZoom() {
557 chrome_page_zoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
558 }
559
InspectTarget(Profile * profile,DevToolsTargetImpl * target)560 static void InspectTarget(Profile* profile, DevToolsTargetImpl* target) {
561 if (target)
562 target->Inspect(profile);
563 }
564
OpenUrlOnRemoteDeviceAndInspect(const std::string & browser_id,const std::string & url)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
Subscribe(const std::string & event_type)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
Unsubscribe(const std::string & event_type)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
EnableRemoteDeviceCounter(bool enable)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
DeviceCountChanged(int count)625 void DevToolsUIBindings::DeviceCountChanged(int count) {
626 base::FundamentalValue value(count);
627 DispatchEventOnFrontend(kDeviceCountChanged, &value);
628 }
629
PopulateRemoteDevices(const std::string & source,scoped_ptr<base::ListValue> targets)630 void DevToolsUIBindings::PopulateRemoteDevices(
631 const std::string& source,
632 scoped_ptr<base::ListValue> targets) {
633 DispatchEventOnFrontend(kDevicesChanged, targets.get());
634 }
635
FileSavedAs(const std::string & url)636 void DevToolsUIBindings::FileSavedAs(const std::string& url) {
637 base::StringValue url_value(url);
638 CallClientFunction("InspectorFrontendAPI.savedURL", &url_value, NULL, NULL);
639 }
640
CanceledFileSaveAs(const std::string & url)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
AppendedTo(const std::string & url)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
FileSystemsLoaded(const std::vector<DevToolsFileHelper::FileSystem> & file_systems)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
FileSystemAdded(const DevToolsFileHelper::FileSystem & file_system)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
IndexingTotalWorkCalculated(int request_id,const std::string & file_system_path,int total_work)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
IndexingWorked(int request_id,const std::string & file_system_path,int worked)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
IndexingDone(int request_id,const std::string & file_system_path)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
SearchCompleted(int request_id,const std::string & file_system_path,const std::vector<std::string> & file_paths)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
ShowDevToolsConfirmInfoBar(const base::string16 & message,const InfoBarCallback & callback)723 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
724 const base::string16& message,
725 const InfoBarCallback& callback) {
726 DevToolsConfirmInfoBarDelegate::Create(delegate_->GetInfoBarService(),
727 callback, message);
728 }
729
UpdateTheme()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
AddDevToolsExtensionsToClient()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
SetDelegate(Delegate * delegate)770 void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
771 delegate_.reset(delegate);
772 }
773
CallClientFunction(const std::string & function_name,const base::Value * arg1,const base::Value * arg2,const base::Value * arg3)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
DispatchEventOnFrontend(const std::string & event_type,const base::Value * event_data)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
DocumentOnLoadCompletedInMainFrame()809 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
810 // Call delegate first - it seeds importants bit of information.
811 delegate_->OnLoadCompleted();
812
813 UpdateTheme();
814 AddDevToolsExtensionsToClient();
815 }
816