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.
5 #include "chrome/browser/devtools/devtools_ui_bindings.h"
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/chrome_notification_types.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/infobars/infobar_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/themes/theme_properties.h"
22 #include "chrome/browser/themes/theme_service.h"
23 #include "chrome/browser/themes/theme_service_factory.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_iterator.h"
26 #include "chrome/browser/ui/browser_list.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #include "chrome/browser/ui/tabs/tab_strip_model.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
31 #include "chrome/common/url_constants.h"
32 #include "chrome/grit/generated_resources.h"
33 #include "components/infobars/core/confirm_infobar_delegate.h"
34 #include "components/infobars/core/infobar.h"
35 #include "components/ui/zoom/page_zoom.h"
36 #include "content/public/browser/favicon_status.h"
37 #include "content/public/browser/invalidate_type.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/navigation_entry.h"
40 #include "content/public/browser/notification_source.h"
41 #include "content/public/browser/render_frame_host.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/user_metrics.h"
44 #include "content/public/browser/web_contents.h"
45 #include "content/public/browser/web_contents_observer.h"
46 #include "content/public/common/renderer_preferences.h"
47 #include "content/public/common/url_constants.h"
48 #include "extensions/browser/extension_registry.h"
49 #include "extensions/common/permissions/permissions_data.h"
50 #include "net/base/io_buffer.h"
51 #include "net/base/net_errors.h"
52 #include "net/http/http_response_headers.h"
53 #include "net/url_request/url_fetcher.h"
54 #include "net/url_request/url_fetcher_response_writer.h"
55 #include "ui/base/l10n/l10n_util.h"
56 #include "ui/base/page_transition_types.h"
58 using base::DictionaryValue
;
59 using content::BrowserThread
;
62 struct LoadCommittedDetails
;
63 struct FrameNavigateParams
;
68 static const char kFrontendHostId
[] = "id";
69 static const char kFrontendHostMethod
[] = "method";
70 static const char kFrontendHostParams
[] = "params";
71 static const char kTitleFormat
[] = "Developer Tools - %s";
73 static const char kDevToolsActionTakenHistogram
[] = "DevTools.ActionTaken";
74 static const char kDevToolsPanelShownHistogram
[] = "DevTools.PanelShown";
76 // This constant should be in sync with
77 // the constant at shell_devtools_frontend.cc.
78 const size_t kMaxMessageChunkSize
= IPC::Channel::kMaximumMessageSize
/ 4;
80 typedef std::vector
<DevToolsUIBindings
*> DevToolsUIBindingsList
;
81 base::LazyInstance
<DevToolsUIBindingsList
>::Leaky g_instances
=
82 LAZY_INSTANCE_INITIALIZER
;
84 std::string
SkColorToRGBAString(SkColor color
) {
85 // We avoid StringPrintf because it will use locale specific formatters for
86 // the double (e.g. ',' instead of '.' in German).
87 return "rgba(" + base::IntToString(SkColorGetR(color
)) + "," +
88 base::IntToString(SkColorGetG(color
)) + "," +
89 base::IntToString(SkColorGetB(color
)) + "," +
90 base::DoubleToString(SkColorGetA(color
) / 255.0) + ")";
93 base::DictionaryValue
* CreateFileSystemValue(
94 DevToolsFileHelper::FileSystem file_system
) {
95 base::DictionaryValue
* file_system_value
= new base::DictionaryValue();
96 file_system_value
->SetString("fileSystemName", file_system
.file_system_name
);
97 file_system_value
->SetString("rootURL", file_system
.root_url
);
98 file_system_value
->SetString("fileSystemPath", file_system
.file_system_path
);
99 return file_system_value
;
102 Browser
* FindBrowser(content::WebContents
* web_contents
) {
103 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
104 int tab_index
= it
->tab_strip_model()->GetIndexOfWebContents(
106 if (tab_index
!= TabStripModel::kNoTab
)
112 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
114 typedef base::Callback
<void(bool)> InfoBarCallback
;
116 class DevToolsConfirmInfoBarDelegate
: public ConfirmInfoBarDelegate
{
118 // If |infobar_service| is NULL, runs |callback| with a single argument with
119 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
120 // and adds the infobar to |infobar_service|.
121 static void Create(InfoBarService
* infobar_service
,
122 const InfoBarCallback
& callback
,
123 const base::string16
& message
);
126 DevToolsConfirmInfoBarDelegate(
127 const InfoBarCallback
& callback
,
128 const base::string16
& message
);
129 ~DevToolsConfirmInfoBarDelegate() override
;
131 base::string16
GetMessageText() const override
;
132 base::string16
GetButtonLabel(InfoBarButton button
) const override
;
133 bool Accept() override
;
134 bool Cancel() override
;
136 InfoBarCallback callback_
;
137 const base::string16 message_
;
139 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate
);
142 void DevToolsConfirmInfoBarDelegate::Create(
143 InfoBarService
* infobar_service
,
144 const InfoBarCallback
& callback
,
145 const base::string16
& message
) {
146 if (!infobar_service
) {
151 infobar_service
->AddInfoBar(
152 infobar_service
->CreateConfirmInfoBar(scoped_ptr
<ConfirmInfoBarDelegate
>(
153 new DevToolsConfirmInfoBarDelegate(callback
, message
))));
156 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
157 const InfoBarCallback
& callback
,
158 const base::string16
& message
)
159 : ConfirmInfoBarDelegate(),
164 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
165 if (!callback_
.is_null())
166 callback_
.Run(false);
169 base::string16
DevToolsConfirmInfoBarDelegate::GetMessageText() const {
173 base::string16
DevToolsConfirmInfoBarDelegate::GetButtonLabel(
174 InfoBarButton button
) const {
175 return l10n_util::GetStringUTF16((button
== BUTTON_OK
) ?
176 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON
: IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON
);
179 bool DevToolsConfirmInfoBarDelegate::Accept() {
185 bool DevToolsConfirmInfoBarDelegate::Cancel() {
186 callback_
.Run(false);
191 // DevToolsUIDefaultDelegate --------------------------------------------------
193 class DefaultBindingsDelegate
: public DevToolsUIBindings::Delegate
{
195 explicit DefaultBindingsDelegate(content::WebContents
* web_contents
)
196 : web_contents_(web_contents
) {}
199 ~DefaultBindingsDelegate() override
{}
201 void ActivateWindow() override
;
202 void CloseWindow() override
{}
203 void SetInspectedPageBounds(const gfx::Rect
& rect
) override
{}
204 void InspectElementCompleted() override
{}
205 void SetIsDocked(bool is_docked
) override
{}
206 void OpenInNewTab(const std::string
& url
) override
;
207 void SetWhitelistedShortcuts(const std::string
& message
) override
{}
209 void InspectedContentsClosing() override
;
210 void OnLoadCompleted() override
{}
211 InfoBarService
* GetInfoBarService() override
;
212 void RenderProcessGone(bool crashed
) override
{}
214 content::WebContents
* web_contents_
;
215 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate
);
218 void DefaultBindingsDelegate::ActivateWindow() {
219 web_contents_
->GetDelegate()->ActivateContents(web_contents_
);
220 web_contents_
->Focus();
223 void DefaultBindingsDelegate::OpenInNewTab(const std::string
& url
) {
224 content::OpenURLParams
params(
225 GURL(url
), content::Referrer(), NEW_FOREGROUND_TAB
,
226 ui::PAGE_TRANSITION_LINK
, false);
227 Browser
* browser
= FindBrowser(web_contents_
);
228 browser
->OpenURL(params
);
231 void DefaultBindingsDelegate::InspectedContentsClosing() {
232 web_contents_
->GetRenderViewHost()->ClosePage();
235 InfoBarService
* DefaultBindingsDelegate::GetInfoBarService() {
236 return InfoBarService::FromWebContents(web_contents_
);
239 // ResponseWriter -------------------------------------------------------------
241 class ResponseWriter
: public net::URLFetcherResponseWriter
{
243 ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
, int stream_id
);
244 ~ResponseWriter() override
;
246 // URLFetcherResponseWriter overrides:
247 int Initialize(const net::CompletionCallback
& callback
) override
;
248 int Write(net::IOBuffer
* buffer
,
250 const net::CompletionCallback
& callback
) override
;
251 int Finish(const net::CompletionCallback
& callback
) override
;
254 base::WeakPtr
<DevToolsUIBindings
> bindings_
;
257 DISALLOW_COPY_AND_ASSIGN(ResponseWriter
);
260 ResponseWriter::ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
,
262 : bindings_(bindings
),
263 stream_id_(stream_id
) {
266 ResponseWriter::~ResponseWriter() {
269 int ResponseWriter::Initialize(const net::CompletionCallback
& callback
) {
273 int ResponseWriter::Write(net::IOBuffer
* buffer
,
275 const net::CompletionCallback
& callback
) {
276 base::FundamentalValue
* id
= new base::FundamentalValue(stream_id_
);
277 base::StringValue
* chunk
=
278 new base::StringValue(std::string(buffer
->data(), num_bytes
));
280 content::BrowserThread::PostTask(
281 content::BrowserThread::UI
, FROM_HERE
,
282 base::Bind(&DevToolsUIBindings::CallClientFunction
,
283 bindings_
, "DevToolsAPI.streamWrite",
284 base::Owned(id
), base::Owned(chunk
), nullptr));
288 int ResponseWriter::Finish(const net::CompletionCallback
& callback
) {
294 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
296 class DevToolsUIBindings::FrontendWebContentsObserver
297 : public content::WebContentsObserver
{
299 explicit FrontendWebContentsObserver(DevToolsUIBindings
* ui_bindings
);
300 ~FrontendWebContentsObserver() override
;
303 // contents::WebContentsObserver:
304 void RenderProcessGone(base::TerminationStatus status
) override
;
305 // TODO(creis): Replace with RenderFrameCreated when http://crbug.com/425397
306 // is fixed. See also http://crbug.com/424641.
307 void AboutToNavigateRenderFrame(
308 content::RenderFrameHost
* old_host
,
309 content::RenderFrameHost
* new_host
) override
;
310 void DocumentOnLoadCompletedInMainFrame() override
;
311 void DidNavigateMainFrame(
312 const content::LoadCommittedDetails
& details
,
313 const content::FrameNavigateParams
& params
) override
;
315 DevToolsUIBindings
* devtools_bindings_
;
316 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver
);
319 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
320 DevToolsUIBindings
* devtools_ui_bindings
)
321 : WebContentsObserver(devtools_ui_bindings
->web_contents()),
322 devtools_bindings_(devtools_ui_bindings
) {
325 DevToolsUIBindings::FrontendWebContentsObserver::
326 ~FrontendWebContentsObserver() {
329 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
330 base::TerminationStatus status
) {
333 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
334 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
335 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
336 if (devtools_bindings_
->agent_host_
.get())
337 devtools_bindings_
->Detach();
343 devtools_bindings_
->delegate_
->RenderProcessGone(crashed
);
346 void DevToolsUIBindings::FrontendWebContentsObserver::
347 AboutToNavigateRenderFrame(content::RenderFrameHost
* old_host
,
348 content::RenderFrameHost
* new_host
) {
349 if (new_host
->GetParent())
351 devtools_bindings_
->frontend_host_
.reset(
352 content::DevToolsFrontendHost::Create(new_host
,
353 devtools_bindings_
));
356 void DevToolsUIBindings::FrontendWebContentsObserver::
357 DocumentOnLoadCompletedInMainFrame() {
358 devtools_bindings_
->DocumentOnLoadCompletedInMainFrame();
361 void DevToolsUIBindings::FrontendWebContentsObserver::
362 DidNavigateMainFrame(const content::LoadCommittedDetails
& details
,
363 const content::FrameNavigateParams
& params
) {
364 devtools_bindings_
->DidNavigateMainFrame();
367 // DevToolsUIBindings ---------------------------------------------------------
369 DevToolsUIBindings
* DevToolsUIBindings::ForWebContents(
370 content::WebContents
* web_contents
) {
371 if (g_instances
== NULL
)
373 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
374 for (DevToolsUIBindingsList::iterator
it(instances
->begin());
375 it
!= instances
->end(); ++it
) {
376 if ((*it
)->web_contents() == web_contents
)
383 GURL
DevToolsUIBindings::ApplyThemeToURL(Profile
* profile
,
384 const GURL
& base_url
) {
385 std::string frontend_url
= base_url
.spec();
386 ThemeService
* tp
= ThemeServiceFactory::GetForProfile(profile
);
388 std::string
url_string(
390 ((frontend_url
.find("?") == std::string::npos
) ? "?" : "&") +
391 "dockSide=undocked" + // TODO(dgozman): remove this support in M38.
393 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
)) +
395 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
)));
396 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
397 switches::kEnableDevToolsExperiments
))
398 url_string
+= "&experiments=true";
399 #if defined(DEBUG_DEVTOOLS)
400 url_string
+= "&debugFrontend=true";
401 #endif // defined(DEBUG_DEVTOOLS)
402 return GURL(url_string
);
405 DevToolsUIBindings::DevToolsUIBindings(content::WebContents
* web_contents
)
406 : profile_(Profile::FromBrowserContext(web_contents
->GetBrowserContext())),
407 android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_
)),
408 web_contents_(web_contents
),
409 delegate_(new DefaultBindingsDelegate(web_contents_
)),
410 devices_updates_enabled_(false),
411 frontend_loaded_(false),
412 weak_factory_(this) {
413 g_instances
.Get().push_back(this);
414 frontend_contents_observer_
.reset(new FrontendWebContentsObserver(this));
415 web_contents_
->GetMutableRendererPrefs()->can_accept_load_drops
= false;
417 file_helper_
.reset(new DevToolsFileHelper(web_contents_
, profile_
));
418 file_system_indexer_
= new DevToolsFileSystemIndexer();
419 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
422 // Wipe out page icon so that the default application icon is used.
423 content::NavigationEntry
* entry
=
424 web_contents_
->GetController().GetActiveEntry();
425 entry
->GetFavicon().image
= gfx::Image();
426 entry
->GetFavicon().valid
= true;
428 // Register on-load actions.
430 this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
431 content::Source
<ThemeService
>(
432 ThemeServiceFactory::GetForProfile(profile_
)));
434 embedder_message_dispatcher_
.reset(
435 DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
437 frontend_host_
.reset(content::DevToolsFrontendHost::Create(
438 web_contents_
->GetMainFrame(), this));
441 DevToolsUIBindings::~DevToolsUIBindings() {
442 for (const auto& pair
: pending_requests_
)
445 if (agent_host_
.get())
446 agent_host_
->DetachClient();
448 for (IndexingJobsMap::const_iterator
jobs_it(indexing_jobs_
.begin());
449 jobs_it
!= indexing_jobs_
.end(); ++jobs_it
) {
450 jobs_it
->second
->Stop();
452 indexing_jobs_
.clear();
453 SetDevicesUpdatesEnabled(false);
455 // Remove self from global list.
456 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
457 DevToolsUIBindingsList::iterator
it(
458 std::find(instances
->begin(), instances
->end(), this));
459 DCHECK(it
!= instances
->end());
460 instances
->erase(it
);
463 // content::NotificationObserver overrides ------------------------------------
464 void DevToolsUIBindings::Observe(int type
,
465 const content::NotificationSource
& source
,
466 const content::NotificationDetails
& details
) {
467 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED
, type
);
471 // content::DevToolsFrontendHost::Delegate implementation ---------------------
472 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
473 const std::string
& message
) {
475 base::ListValue empty_params
;
476 base::ListValue
* params
= &empty_params
;
478 base::DictionaryValue
* dict
= NULL
;
479 scoped_ptr
<base::Value
> parsed_message(base::JSONReader::Read(message
));
480 if (!parsed_message
||
481 !parsed_message
->GetAsDictionary(&dict
) ||
482 !dict
->GetString(kFrontendHostMethod
, &method
) ||
483 (dict
->HasKey(kFrontendHostParams
) &&
484 !dict
->GetList(kFrontendHostParams
, ¶ms
))) {
485 LOG(ERROR
) << "Invalid message was sent to embedder: " << message
;
489 dict
->GetInteger(kFrontendHostId
, &id
);
490 embedder_message_dispatcher_
->Dispatch(
491 base::Bind(&DevToolsUIBindings::SendMessageAck
,
492 weak_factory_
.GetWeakPtr(),
498 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
499 const std::string
& message
) {
500 if (agent_host_
.get())
501 agent_host_
->DispatchProtocolMessage(message
);
504 // content::DevToolsAgentHostClient implementation --------------------------
505 void DevToolsUIBindings::DispatchProtocolMessage(
506 content::DevToolsAgentHost
* agent_host
, const std::string
& message
) {
507 DCHECK(agent_host
== agent_host_
.get());
509 if (message
.length() < kMaxMessageChunkSize
) {
510 base::string16 javascript
= base::UTF8ToUTF16(
511 "DevToolsAPI.dispatchMessage(" + message
+ ");");
512 web_contents_
->GetMainFrame()->ExecuteJavaScript(javascript
);
516 base::FundamentalValue
total_size(static_cast<int>(message
.length()));
517 for (size_t pos
= 0; pos
< message
.length(); pos
+= kMaxMessageChunkSize
) {
518 base::StringValue
message_value(message
.substr(pos
, kMaxMessageChunkSize
));
519 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
520 &message_value
, pos
? NULL
: &total_size
, NULL
);
524 void DevToolsUIBindings::AgentHostClosed(
525 content::DevToolsAgentHost
* agent_host
,
526 bool replaced_with_another_client
) {
527 DCHECK(agent_host
== agent_host_
.get());
529 delegate_
->InspectedContentsClosing();
532 void DevToolsUIBindings::SendMessageAck(int request_id
,
533 const base::Value
* arg
) {
534 base::FundamentalValue
id_value(request_id
);
535 CallClientFunction("DevToolsAPI.embedderMessageAck",
536 &id_value
, arg
, nullptr);
539 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
540 void DevToolsUIBindings::ActivateWindow() {
541 delegate_
->ActivateWindow();
544 void DevToolsUIBindings::CloseWindow() {
545 delegate_
->CloseWindow();
548 void DevToolsUIBindings::LoadCompleted() {
552 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect
& rect
) {
553 delegate_
->SetInspectedPageBounds(rect
);
556 void DevToolsUIBindings::SetIsDocked(const DispatchCallback
& callback
,
557 bool dock_requested
) {
558 delegate_
->SetIsDocked(dock_requested
);
559 callback
.Run(nullptr);
562 void DevToolsUIBindings::InspectElementCompleted() {
563 delegate_
->InspectElementCompleted();
566 void DevToolsUIBindings::InspectedURLChanged(const std::string
& url
) {
567 content::NavigationController
& controller
= web_contents()->GetController();
568 content::NavigationEntry
* entry
= controller
.GetActiveEntry();
569 // DevTools UI is not localized.
571 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat
, url
.c_str())));
572 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE
);
575 void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback
& callback
,
576 const std::string
& url
,
577 const std::string
& headers
,
580 if (!gurl
.is_valid()) {
581 base::DictionaryValue response
;
582 response
.SetInteger("statusCode", 404);
583 callback
.Run(&response
);
587 net::URLFetcher
* fetcher
=
588 net::URLFetcher::Create(gurl
, net::URLFetcher::GET
, this);
589 pending_requests_
[fetcher
] = callback
;
590 fetcher
->SetRequestContext(profile_
->GetRequestContext());
591 fetcher
->SetExtraRequestHeaders(headers
);
592 fetcher
->SaveResponseWithWriter(scoped_ptr
<net::URLFetcherResponseWriter
>(
593 new ResponseWriter(weak_factory_
.GetWeakPtr(), stream_id
)));
597 void DevToolsUIBindings::OpenInNewTab(const std::string
& url
) {
598 delegate_
->OpenInNewTab(url
);
601 void DevToolsUIBindings::SaveToFile(const std::string
& url
,
602 const std::string
& content
,
604 file_helper_
->Save(url
, content
, save_as
,
605 base::Bind(&DevToolsUIBindings::FileSavedAs
,
606 weak_factory_
.GetWeakPtr(), url
),
607 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs
,
608 weak_factory_
.GetWeakPtr(), url
));
611 void DevToolsUIBindings::AppendToFile(const std::string
& url
,
612 const std::string
& content
) {
613 file_helper_
->Append(url
, content
,
614 base::Bind(&DevToolsUIBindings::AppendedTo
,
615 weak_factory_
.GetWeakPtr(), url
));
618 void DevToolsUIBindings::RequestFileSystems() {
619 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
620 file_helper_
->RequestFileSystems(base::Bind(
621 &DevToolsUIBindings::FileSystemsLoaded
, weak_factory_
.GetWeakPtr()));
624 void DevToolsUIBindings::AddFileSystem() {
625 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
626 file_helper_
->AddFileSystem(
627 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
628 weak_factory_
.GetWeakPtr()),
629 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
630 weak_factory_
.GetWeakPtr()));
633 void DevToolsUIBindings::RemoveFileSystem(const std::string
& file_system_path
) {
634 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
635 file_helper_
->RemoveFileSystem(file_system_path
);
636 base::StringValue
file_system_path_value(file_system_path
);
637 CallClientFunction("DevToolsAPI.fileSystemRemoved",
638 &file_system_path_value
, NULL
, NULL
);
641 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
642 const std::string
& file_system_url
) {
643 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
644 file_helper_
->UpgradeDraggedFileSystemPermissions(
646 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
647 weak_factory_
.GetWeakPtr()),
648 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
649 weak_factory_
.GetWeakPtr()));
652 void DevToolsUIBindings::IndexPath(int index_request_id
,
653 const std::string
& file_system_path
) {
654 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
655 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
656 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
657 IndexingDone(index_request_id
, file_system_path
);
660 if (indexing_jobs_
.count(index_request_id
) != 0)
662 indexing_jobs_
[index_request_id
] =
663 scoped_refptr
<DevToolsFileSystemIndexer::FileSystemIndexingJob
>(
664 file_system_indexer_
->IndexPath(
666 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated
,
667 weak_factory_
.GetWeakPtr(),
670 Bind(&DevToolsUIBindings::IndexingWorked
,
671 weak_factory_
.GetWeakPtr(),
674 Bind(&DevToolsUIBindings::IndexingDone
,
675 weak_factory_
.GetWeakPtr(),
680 void DevToolsUIBindings::StopIndexing(int index_request_id
) {
681 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
682 IndexingJobsMap::iterator it
= indexing_jobs_
.find(index_request_id
);
683 if (it
== indexing_jobs_
.end())
686 indexing_jobs_
.erase(it
);
689 void DevToolsUIBindings::SearchInPath(int search_request_id
,
690 const std::string
& file_system_path
,
691 const std::string
& query
) {
692 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
693 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
694 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
695 SearchCompleted(search_request_id
,
697 std::vector
<std::string
>());
700 file_system_indexer_
->SearchInPath(file_system_path
,
702 Bind(&DevToolsUIBindings::SearchCompleted
,
703 weak_factory_
.GetWeakPtr(),
708 void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string
& message
) {
709 delegate_
->SetWhitelistedShortcuts(message
);
712 void DevToolsUIBindings::ZoomIn() {
713 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN
);
716 void DevToolsUIBindings::ZoomOut() {
717 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT
);
720 void DevToolsUIBindings::ResetZoom() {
721 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET
);
724 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled
) {
725 if (devices_updates_enabled_
== enabled
)
727 devices_updates_enabled_
= enabled
;
729 remote_targets_handler_
= DevToolsTargetsUIHandler::CreateForAdb(
730 base::Bind(&DevToolsUIBindings::DevicesUpdated
,
731 base::Unretained(this)),
734 remote_targets_handler_
.reset();
738 void DevToolsUIBindings::SendMessageToBrowser(const std::string
& message
) {
739 if (agent_host_
.get())
740 agent_host_
->DispatchProtocolMessage(message
);
743 void DevToolsUIBindings::RecordEnumeratedHistogram(const std::string
& name
,
745 int boundary_value
) {
746 if (!(boundary_value
>= 0 && boundary_value
< 100 && sample
>= 0 &&
747 sample
< boundary_value
)) {
748 frontend_host_
->BadMessageRecieved();
751 // Each histogram name must follow a different code path in
752 // order to UMA_HISTOGRAM_ENUMERATION work correctly.
753 if (name
== kDevToolsActionTakenHistogram
)
754 UMA_HISTOGRAM_ENUMERATION(name
, sample
, boundary_value
);
755 else if (name
== kDevToolsPanelShownHistogram
)
756 UMA_HISTOGRAM_ENUMERATION(name
, sample
, boundary_value
);
758 frontend_host_
->BadMessageRecieved();
761 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback
& callback
,
762 const std::string
& browser_id
,
763 const std::string
& url
) {
764 if (!android_bridge_
) {
765 callback
.Run(nullptr);
768 android_bridge_
->SendJsonRequest(browser_id
, url
,
769 base::Bind(&DevToolsUIBindings::JsonReceived
,
770 weak_factory_
.GetWeakPtr(),
774 void DevToolsUIBindings::JsonReceived(const DispatchCallback
& callback
,
776 const std::string
& message
) {
777 if (result
!= net::OK
) {
778 callback
.Run(nullptr);
781 base::StringValue
message_value(message
);
782 callback
.Run(&message_value
);
785 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher
* source
) {
787 PendingRequestsMap::iterator it
= pending_requests_
.find(source
);
788 DCHECK(it
!= pending_requests_
.end());
790 base::DictionaryValue response
;
791 base::DictionaryValue
* headers
= new base::DictionaryValue();
792 net::HttpResponseHeaders
* rh
= source
->GetResponseHeaders();
793 response
.SetInteger("statusCode", rh
? rh
->response_code() : 200);
794 response
.Set("headers", headers
);
796 void* iterator
= NULL
;
799 while (rh
&& rh
->EnumerateHeaderLines(&iterator
, &name
, &value
))
800 headers
->SetString(name
, value
);
802 it
->second
.Run(&response
);
803 pending_requests_
.erase(it
);
807 void DevToolsUIBindings::DeviceCountChanged(int count
) {
808 base::FundamentalValue
value(count
);
809 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value
, NULL
,
813 void DevToolsUIBindings::DevicesUpdated(
814 const std::string
& source
,
815 const base::ListValue
& targets
) {
816 CallClientFunction("DevToolsAPI.devicesUpdated", &targets
, NULL
,
820 void DevToolsUIBindings::FileSavedAs(const std::string
& url
) {
821 base::StringValue
url_value(url
);
822 CallClientFunction("DevToolsAPI.savedURL", &url_value
, NULL
, NULL
);
825 void DevToolsUIBindings::CanceledFileSaveAs(const std::string
& url
) {
826 base::StringValue
url_value(url
);
827 CallClientFunction("DevToolsAPI.canceledSaveURL",
828 &url_value
, NULL
, NULL
);
831 void DevToolsUIBindings::AppendedTo(const std::string
& url
) {
832 base::StringValue
url_value(url
);
833 CallClientFunction("DevToolsAPI.appendedToURL", &url_value
, NULL
,
837 void DevToolsUIBindings::FileSystemsLoaded(
838 const std::vector
<DevToolsFileHelper::FileSystem
>& file_systems
) {
839 base::ListValue file_systems_value
;
840 for (size_t i
= 0; i
< file_systems
.size(); ++i
)
841 file_systems_value
.Append(CreateFileSystemValue(file_systems
[i
]));
842 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
843 &file_systems_value
, NULL
, NULL
);
846 void DevToolsUIBindings::FileSystemAdded(
847 const DevToolsFileHelper::FileSystem
& file_system
) {
848 scoped_ptr
<base::StringValue
> error_string_value(
849 new base::StringValue(std::string()));
850 scoped_ptr
<base::DictionaryValue
> file_system_value
;
851 if (!file_system
.file_system_path
.empty())
852 file_system_value
.reset(CreateFileSystemValue(file_system
));
853 CallClientFunction("DevToolsAPI.fileSystemAdded",
854 error_string_value
.get(), file_system_value
.get(), NULL
);
857 void DevToolsUIBindings::IndexingTotalWorkCalculated(
859 const std::string
& file_system_path
,
861 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
862 base::FundamentalValue
request_id_value(request_id
);
863 base::StringValue
file_system_path_value(file_system_path
);
864 base::FundamentalValue
total_work_value(total_work
);
865 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
866 &request_id_value
, &file_system_path_value
,
870 void DevToolsUIBindings::IndexingWorked(int request_id
,
871 const std::string
& file_system_path
,
873 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
874 base::FundamentalValue
request_id_value(request_id
);
875 base::StringValue
file_system_path_value(file_system_path
);
876 base::FundamentalValue
worked_value(worked
);
877 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value
,
878 &file_system_path_value
, &worked_value
);
881 void DevToolsUIBindings::IndexingDone(int request_id
,
882 const std::string
& file_system_path
) {
883 indexing_jobs_
.erase(request_id
);
884 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
885 base::FundamentalValue
request_id_value(request_id
);
886 base::StringValue
file_system_path_value(file_system_path
);
887 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value
,
888 &file_system_path_value
, NULL
);
891 void DevToolsUIBindings::SearchCompleted(
893 const std::string
& file_system_path
,
894 const std::vector
<std::string
>& file_paths
) {
895 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
896 base::ListValue file_paths_value
;
897 for (std::vector
<std::string
>::const_iterator
it(file_paths
.begin());
898 it
!= file_paths
.end(); ++it
) {
899 file_paths_value
.AppendString(*it
);
901 base::FundamentalValue
request_id_value(request_id
);
902 base::StringValue
file_system_path_value(file_system_path
);
903 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value
,
904 &file_system_path_value
, &file_paths_value
);
907 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
908 const base::string16
& message
,
909 const InfoBarCallback
& callback
) {
910 DevToolsConfirmInfoBarDelegate::Create(delegate_
->GetInfoBarService(),
914 void DevToolsUIBindings::UpdateTheme() {
915 ThemeService
* tp
= ThemeServiceFactory::GetForProfile(profile_
);
918 std::string
command("DevToolsAPI.setToolbarColors(\"" +
919 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
)) +
921 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
)) +
923 web_contents_
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(command
));
926 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
927 const extensions::ExtensionRegistry
* registry
=
928 extensions::ExtensionRegistry::Get(profile_
->GetOriginalProfile());
932 base::ListValue results
;
933 for (const scoped_refptr
<const extensions::Extension
>& extension
:
934 registry
->enabled_extensions()) {
935 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension
.get())
938 base::DictionaryValue
* extension_info
= new base::DictionaryValue();
941 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
942 extension
.get()).spec()));
943 extension_info
->Set("name", new base::StringValue(extension
->name()));
944 extension_info
->Set("exposeExperimentalAPIs",
945 new base::FundamentalValue(
946 extension
->permissions_data()->HasAPIPermission(
947 extensions::APIPermission::kExperimental
)));
948 results
.Append(extension_info
);
950 CallClientFunction("DevToolsAPI.addExtensions",
951 &results
, NULL
, NULL
);
954 void DevToolsUIBindings::SetDelegate(Delegate
* delegate
) {
955 delegate_
.reset(delegate
);
958 void DevToolsUIBindings::AttachTo(
959 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
) {
960 if (agent_host_
.get())
962 agent_host_
= agent_host
;
963 agent_host_
->AttachClient(this);
966 void DevToolsUIBindings::Reattach() {
967 DCHECK(agent_host_
.get());
968 agent_host_
->DetachClient();
969 agent_host_
->AttachClient(this);
972 void DevToolsUIBindings::Detach() {
973 if (agent_host_
.get())
974 agent_host_
->DetachClient();
978 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost
* agent_host
) {
979 return agent_host_
.get() == agent_host
;
982 void DevToolsUIBindings::CallClientFunction(const std::string
& function_name
,
983 const base::Value
* arg1
,
984 const base::Value
* arg2
,
985 const base::Value
* arg3
) {
986 std::string javascript
= function_name
+ "(";
989 base::JSONWriter::Write(arg1
, &json
);
990 javascript
.append(json
);
992 base::JSONWriter::Write(arg2
, &json
);
993 javascript
.append(", ").append(json
);
995 base::JSONWriter::Write(arg3
, &json
);
996 javascript
.append(", ").append(json
);
1000 javascript
.append(");");
1001 web_contents_
->GetMainFrame()->ExecuteJavaScript(
1002 base::UTF8ToUTF16(javascript
));
1005 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
1006 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
1007 // arrives before the LoadCompleted event, thus it should not trigger the
1008 // frontend load handling.
1009 #if !defined(DEBUG_DEVTOOLS)
1014 void DevToolsUIBindings::DidNavigateMainFrame() {
1015 frontend_loaded_
= false;
1018 void DevToolsUIBindings::FrontendLoaded() {
1019 if (frontend_loaded_
)
1021 frontend_loaded_
= true;
1023 // Call delegate first - it seeds importants bit of information.
1024 delegate_
->OnLoadCompleted();
1027 AddDevToolsExtensionsToClient();