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/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/metrics/histogram.h"
10 #include "base/prefs/scoped_user_pref_update.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/ui/browser.h"
22 #include "chrome/browser/ui/browser_iterator.h"
23 #include "chrome/browser/ui/browser_list.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/common/url_constants.h"
30 #include "chrome/grit/generated_resources.h"
31 #include "components/infobars/core/confirm_infobar_delegate.h"
32 #include "components/infobars/core/infobar.h"
33 #include "components/syncable_prefs/pref_service_syncable.h"
34 #include "components/ui/zoom/page_zoom.h"
35 #include "content/public/browser/devtools_external_agent_proxy.h"
36 #include "content/public/browser/devtools_external_agent_proxy_delegate.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 static const char kRemotePageActionInspect
[] = "inspect";
77 static const char kRemotePageActionReload
[] = "reload";
78 static const char kRemotePageActionActivate
[] = "activate";
79 static const char kRemotePageActionClose
[] = "close";
81 // This constant should be in sync with
82 // the constant at shell_devtools_frontend.cc.
83 const size_t kMaxMessageChunkSize
= IPC::Channel::kMaximumMessageSize
/ 4;
85 typedef std::vector
<DevToolsUIBindings
*> DevToolsUIBindingsList
;
86 base::LazyInstance
<DevToolsUIBindingsList
>::Leaky g_instances
=
87 LAZY_INSTANCE_INITIALIZER
;
89 base::DictionaryValue
* CreateFileSystemValue(
90 DevToolsFileHelper::FileSystem file_system
) {
91 base::DictionaryValue
* file_system_value
= new base::DictionaryValue();
92 file_system_value
->SetString("fileSystemName", file_system
.file_system_name
);
93 file_system_value
->SetString("rootURL", file_system
.root_url
);
94 file_system_value
->SetString("fileSystemPath", file_system
.file_system_path
);
95 return file_system_value
;
98 Browser
* FindBrowser(content::WebContents
* web_contents
) {
99 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
100 int tab_index
= it
->tab_strip_model()->GetIndexOfWebContents(
102 if (tab_index
!= TabStripModel::kNoTab
)
108 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
110 typedef base::Callback
<void(bool)> InfoBarCallback
;
112 class DevToolsConfirmInfoBarDelegate
: public ConfirmInfoBarDelegate
{
114 // If |infobar_service| is NULL, runs |callback| with a single argument with
115 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
116 // and adds the infobar to |infobar_service|.
117 static void Create(InfoBarService
* infobar_service
,
118 const InfoBarCallback
& callback
,
119 const base::string16
& message
);
122 DevToolsConfirmInfoBarDelegate(
123 const InfoBarCallback
& callback
,
124 const base::string16
& message
);
125 ~DevToolsConfirmInfoBarDelegate() override
;
127 base::string16
GetMessageText() const override
;
128 base::string16
GetButtonLabel(InfoBarButton button
) const override
;
129 bool Accept() override
;
130 bool Cancel() override
;
132 InfoBarCallback callback_
;
133 const base::string16 message_
;
135 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate
);
138 void DevToolsConfirmInfoBarDelegate::Create(
139 InfoBarService
* infobar_service
,
140 const InfoBarCallback
& callback
,
141 const base::string16
& message
) {
142 if (!infobar_service
) {
147 infobar_service
->AddInfoBar(
148 infobar_service
->CreateConfirmInfoBar(scoped_ptr
<ConfirmInfoBarDelegate
>(
149 new DevToolsConfirmInfoBarDelegate(callback
, message
))));
152 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
153 const InfoBarCallback
& callback
,
154 const base::string16
& message
)
155 : ConfirmInfoBarDelegate(),
160 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
161 if (!callback_
.is_null())
162 callback_
.Run(false);
165 base::string16
DevToolsConfirmInfoBarDelegate::GetMessageText() const {
169 base::string16
DevToolsConfirmInfoBarDelegate::GetButtonLabel(
170 InfoBarButton button
) const {
171 return l10n_util::GetStringUTF16((button
== BUTTON_OK
) ?
172 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON
: IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON
);
175 bool DevToolsConfirmInfoBarDelegate::Accept() {
181 bool DevToolsConfirmInfoBarDelegate::Cancel() {
182 callback_
.Run(false);
187 // DevToolsUIDefaultDelegate --------------------------------------------------
189 class DefaultBindingsDelegate
: public DevToolsUIBindings::Delegate
{
191 explicit DefaultBindingsDelegate(content::WebContents
* web_contents
)
192 : web_contents_(web_contents
) {}
195 ~DefaultBindingsDelegate() override
{}
197 void ActivateWindow() override
;
198 void CloseWindow() override
{}
199 void SetInspectedPageBounds(const gfx::Rect
& rect
) override
{}
200 void InspectElementCompleted() override
{}
201 void SetIsDocked(bool is_docked
) override
{}
202 void OpenInNewTab(const std::string
& url
) override
;
203 void SetWhitelistedShortcuts(const std::string
& message
) override
{}
204 using DispatchCallback
=
205 DevToolsEmbedderMessageDispatcher::Delegate::DispatchCallback
;
207 void InspectedContentsClosing() override
;
208 void OnLoadCompleted() override
{}
209 InfoBarService
* GetInfoBarService() override
;
210 void RenderProcessGone(bool crashed
) override
{}
212 content::WebContents
* web_contents_
;
213 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate
);
216 void DefaultBindingsDelegate::ActivateWindow() {
217 web_contents_
->GetDelegate()->ActivateContents(web_contents_
);
218 web_contents_
->Focus();
221 void DefaultBindingsDelegate::OpenInNewTab(const std::string
& url
) {
222 content::OpenURLParams
params(
223 GURL(url
), content::Referrer(), NEW_FOREGROUND_TAB
,
224 ui::PAGE_TRANSITION_LINK
, false);
225 Browser
* browser
= FindBrowser(web_contents_
);
226 browser
->OpenURL(params
);
229 void DefaultBindingsDelegate::InspectedContentsClosing() {
230 web_contents_
->ClosePage();
233 InfoBarService
* DefaultBindingsDelegate::GetInfoBarService() {
234 return InfoBarService::FromWebContents(web_contents_
);
237 // ResponseWriter -------------------------------------------------------------
239 class ResponseWriter
: public net::URLFetcherResponseWriter
{
241 ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
, int stream_id
);
242 ~ResponseWriter() override
;
244 // URLFetcherResponseWriter overrides:
245 int Initialize(const net::CompletionCallback
& callback
) override
;
246 int Write(net::IOBuffer
* buffer
,
248 const net::CompletionCallback
& callback
) override
;
249 int Finish(const net::CompletionCallback
& callback
) override
;
252 base::WeakPtr
<DevToolsUIBindings
> bindings_
;
255 DISALLOW_COPY_AND_ASSIGN(ResponseWriter
);
258 ResponseWriter::ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
,
260 : bindings_(bindings
),
261 stream_id_(stream_id
) {
264 ResponseWriter::~ResponseWriter() {
267 int ResponseWriter::Initialize(const net::CompletionCallback
& callback
) {
271 int ResponseWriter::Write(net::IOBuffer
* buffer
,
273 const net::CompletionCallback
& callback
) {
274 base::FundamentalValue
* id
= new base::FundamentalValue(stream_id_
);
275 base::StringValue
* chunk
=
276 new base::StringValue(std::string(buffer
->data(), num_bytes
));
278 content::BrowserThread::PostTask(
279 content::BrowserThread::UI
, FROM_HERE
,
280 base::Bind(&DevToolsUIBindings::CallClientFunction
,
281 bindings_
, "DevToolsAPI.streamWrite",
282 base::Owned(id
), base::Owned(chunk
), nullptr));
286 int ResponseWriter::Finish(const net::CompletionCallback
& callback
) {
292 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
294 class DevToolsUIBindings::FrontendWebContentsObserver
295 : public content::WebContentsObserver
{
297 explicit FrontendWebContentsObserver(DevToolsUIBindings
* ui_bindings
);
298 ~FrontendWebContentsObserver() override
;
301 // contents::WebContentsObserver:
302 void RenderProcessGone(base::TerminationStatus status
) override
;
303 // TODO(creis): Replace with RenderFrameCreated when http://crbug.com/425397
304 // is fixed. See also http://crbug.com/424641.
305 void AboutToNavigateRenderFrame(
306 content::RenderFrameHost
* old_host
,
307 content::RenderFrameHost
* new_host
) override
;
308 void DocumentOnLoadCompletedInMainFrame() override
;
309 void DidNavigateMainFrame(
310 const content::LoadCommittedDetails
& details
,
311 const content::FrameNavigateParams
& params
) override
;
313 DevToolsUIBindings
* devtools_bindings_
;
314 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver
);
317 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
318 DevToolsUIBindings
* devtools_ui_bindings
)
319 : WebContentsObserver(devtools_ui_bindings
->web_contents()),
320 devtools_bindings_(devtools_ui_bindings
) {
323 DevToolsUIBindings::FrontendWebContentsObserver::
324 ~FrontendWebContentsObserver() {
327 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
328 base::TerminationStatus status
) {
331 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
332 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
333 #if defined(OS_CHROMEOS)
334 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM
:
336 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
337 case base::TERMINATION_STATUS_LAUNCH_FAILED
:
338 if (devtools_bindings_
->agent_host_
.get())
339 devtools_bindings_
->Detach();
345 devtools_bindings_
->delegate_
->RenderProcessGone(crashed
);
348 void DevToolsUIBindings::FrontendWebContentsObserver::
349 AboutToNavigateRenderFrame(content::RenderFrameHost
* old_host
,
350 content::RenderFrameHost
* new_host
) {
351 if (new_host
->GetParent())
353 devtools_bindings_
->frontend_host_
.reset(
354 content::DevToolsFrontendHost::Create(new_host
,
355 devtools_bindings_
));
358 void DevToolsUIBindings::FrontendWebContentsObserver::
359 DocumentOnLoadCompletedInMainFrame() {
360 devtools_bindings_
->DocumentOnLoadCompletedInMainFrame();
363 void DevToolsUIBindings::FrontendWebContentsObserver::
364 DidNavigateMainFrame(const content::LoadCommittedDetails
& details
,
365 const content::FrameNavigateParams
& params
) {
366 devtools_bindings_
->DidNavigateMainFrame();
369 // WebSocketAPIChannel --------------------------------------------------------
371 class DevToolsUIBindings::WebSocketAPIChannel
:
372 public content::DevToolsExternalAgentProxyDelegate
{
374 explicit WebSocketAPIChannel(base::WeakPtr
<DevToolsUIBindings
> bindings
);
375 ~WebSocketAPIChannel() override
;
376 void DispatchOnClientHost(const std::string
& message
);
377 void ConnectionClosed();
380 // content::DevToolsExternalAgentProxyDelegate implementation.
381 void Attach(content::DevToolsExternalAgentProxy
* proxy
) override
;
382 void Detach() override
;
383 void SendMessageToBackend(const std::string
& message
) override
;
385 base::WeakPtr
<DevToolsUIBindings
> bindings_
;
386 content::DevToolsExternalAgentProxy
* attached_proxy_
;
387 DISALLOW_COPY_AND_ASSIGN(WebSocketAPIChannel
);
390 DevToolsUIBindings::WebSocketAPIChannel::WebSocketAPIChannel(
391 base::WeakPtr
<DevToolsUIBindings
> bindings
)
392 : bindings_(bindings
),
393 attached_proxy_(nullptr) {
394 bindings
->open_api_channel_
= this;
397 DevToolsUIBindings::WebSocketAPIChannel::~WebSocketAPIChannel() {
399 bindings_
->open_api_channel_
= nullptr;
402 void DevToolsUIBindings::WebSocketAPIChannel::DispatchOnClientHost(
403 const std::string
& message
) {
405 attached_proxy_
->DispatchOnClientHost(message
);
408 void DevToolsUIBindings::WebSocketAPIChannel::ConnectionClosed() {
410 bindings_
->CallClientFunction("DevToolsAPI.frontendAPIDetached",
411 nullptr, nullptr, nullptr);
414 attached_proxy_
->ConnectionClosed();
417 void DevToolsUIBindings::WebSocketAPIChannel::Attach(
418 content::DevToolsExternalAgentProxy
* proxy
) {
419 attached_proxy_
= proxy
;
421 bindings_
->CallClientFunction("DevToolsAPI.frontendAPIAttached",
422 nullptr, nullptr, nullptr);
426 void DevToolsUIBindings::WebSocketAPIChannel::Detach() {
427 attached_proxy_
= nullptr;
429 bindings_
->open_api_channel_
= nullptr;
430 bindings_
->CallClientFunction("DevToolsAPI.frontendAPIDetached",
431 nullptr, nullptr, nullptr);
435 void DevToolsUIBindings::WebSocketAPIChannel::SendMessageToBackend(
436 const std::string
& message
) {
439 base::StringValue
message_value(message
);
440 bindings_
->CallClientFunction("DevToolsAPI.dispatchFrontendAPIMessage",
441 &message_value
, nullptr, nullptr);
444 // DevToolsUIBindings ---------------------------------------------------------
446 DevToolsUIBindings
* DevToolsUIBindings::ForWebContents(
447 content::WebContents
* web_contents
) {
448 if (g_instances
== NULL
)
450 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
451 for (DevToolsUIBindingsList::iterator
it(instances
->begin());
452 it
!= instances
->end(); ++it
) {
453 if ((*it
)->web_contents() == web_contents
)
459 DevToolsUIBindings::DevToolsUIBindings(content::WebContents
* web_contents
)
460 : profile_(Profile::FromBrowserContext(web_contents
->GetBrowserContext())),
461 android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_
)),
462 web_contents_(web_contents
),
463 delegate_(new DefaultBindingsDelegate(web_contents_
)),
464 devices_updates_enabled_(false),
465 frontend_loaded_(false),
466 open_api_channel_(nullptr),
467 weak_factory_(this) {
468 g_instances
.Get().push_back(this);
469 frontend_contents_observer_
.reset(new FrontendWebContentsObserver(this));
470 web_contents_
->GetMutableRendererPrefs()->can_accept_load_drops
= false;
472 file_helper_
.reset(new DevToolsFileHelper(web_contents_
, profile_
));
473 file_system_indexer_
= new DevToolsFileSystemIndexer();
474 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
477 // Register on-load actions.
478 embedder_message_dispatcher_
.reset(
479 DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
481 frontend_host_
.reset(content::DevToolsFrontendHost::Create(
482 web_contents_
->GetMainFrame(), this));
485 DevToolsUIBindings::~DevToolsUIBindings() {
486 for (const auto& pair
: pending_requests_
)
489 if (agent_host_
.get())
490 agent_host_
->DetachClient();
492 for (IndexingJobsMap::const_iterator
jobs_it(indexing_jobs_
.begin());
493 jobs_it
!= indexing_jobs_
.end(); ++jobs_it
) {
494 jobs_it
->second
->Stop();
496 indexing_jobs_
.clear();
497 SetDevicesUpdatesEnabled(false);
499 if (open_api_channel_
)
500 open_api_channel_
->ConnectionClosed();
502 // Remove self from global list.
503 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
504 DevToolsUIBindingsList::iterator
it(
505 std::find(instances
->begin(), instances
->end(), this));
506 DCHECK(it
!= instances
->end());
507 instances
->erase(it
);
510 // content::DevToolsFrontendHost::Delegate implementation ---------------------
511 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
512 const std::string
& message
) {
514 base::ListValue empty_params
;
515 base::ListValue
* params
= &empty_params
;
517 base::DictionaryValue
* dict
= NULL
;
518 scoped_ptr
<base::Value
> parsed_message
= base::JSONReader::Read(message
);
519 if (!parsed_message
||
520 !parsed_message
->GetAsDictionary(&dict
) ||
521 !dict
->GetString(kFrontendHostMethod
, &method
) ||
522 (dict
->HasKey(kFrontendHostParams
) &&
523 !dict
->GetList(kFrontendHostParams
, ¶ms
))) {
524 LOG(ERROR
) << "Invalid message was sent to embedder: " << message
;
528 dict
->GetInteger(kFrontendHostId
, &id
);
529 embedder_message_dispatcher_
->Dispatch(
530 base::Bind(&DevToolsUIBindings::SendMessageAck
,
531 weak_factory_
.GetWeakPtr(),
537 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
538 const std::string
& message
) {
539 if (agent_host_
.get())
540 agent_host_
->DispatchProtocolMessage(message
);
543 // content::DevToolsAgentHostClient implementation --------------------------
544 void DevToolsUIBindings::DispatchProtocolMessage(
545 content::DevToolsAgentHost
* agent_host
, const std::string
& message
) {
546 DCHECK(agent_host
== agent_host_
.get());
548 if (message
.length() < kMaxMessageChunkSize
) {
549 base::string16 javascript
= base::UTF8ToUTF16(
550 "DevToolsAPI.dispatchMessage(" + message
+ ");");
551 web_contents_
->GetMainFrame()->ExecuteJavaScript(javascript
);
555 base::FundamentalValue
total_size(static_cast<int>(message
.length()));
556 for (size_t pos
= 0; pos
< message
.length(); pos
+= kMaxMessageChunkSize
) {
557 base::StringValue
message_value(message
.substr(pos
, kMaxMessageChunkSize
));
558 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
559 &message_value
, pos
? NULL
: &total_size
, NULL
);
563 void DevToolsUIBindings::AgentHostClosed(
564 content::DevToolsAgentHost
* agent_host
,
565 bool replaced_with_another_client
) {
566 DCHECK(agent_host
== agent_host_
.get());
568 delegate_
->InspectedContentsClosing();
571 void DevToolsUIBindings::SendMessageAck(int request_id
,
572 const base::Value
* arg
) {
573 base::FundamentalValue
id_value(request_id
);
574 CallClientFunction("DevToolsAPI.embedderMessageAck",
575 &id_value
, arg
, nullptr);
578 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
579 void DevToolsUIBindings::ActivateWindow() {
580 delegate_
->ActivateWindow();
583 void DevToolsUIBindings::CloseWindow() {
584 delegate_
->CloseWindow();
587 void DevToolsUIBindings::LoadCompleted() {
591 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect
& rect
) {
592 delegate_
->SetInspectedPageBounds(rect
);
595 void DevToolsUIBindings::SetIsDocked(const DispatchCallback
& callback
,
596 bool dock_requested
) {
597 delegate_
->SetIsDocked(dock_requested
);
598 callback
.Run(nullptr);
601 void DevToolsUIBindings::InspectElementCompleted() {
602 delegate_
->InspectElementCompleted();
605 void DevToolsUIBindings::InspectedURLChanged(const std::string
& url
) {
606 content::NavigationController
& controller
= web_contents()->GetController();
607 content::NavigationEntry
* entry
= controller
.GetActiveEntry();
608 // DevTools UI is not localized.
610 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat
, url
.c_str())));
611 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE
);
614 void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback
& callback
,
615 const std::string
& url
,
616 const std::string
& headers
,
619 if (!gurl
.is_valid()) {
620 base::DictionaryValue response
;
621 response
.SetInteger("statusCode", 404);
622 callback
.Run(&response
);
626 net::URLFetcher
* fetcher
=
627 net::URLFetcher::Create(gurl
, net::URLFetcher::GET
, this).release();
628 pending_requests_
[fetcher
] = callback
;
629 fetcher
->SetRequestContext(profile_
->GetRequestContext());
630 fetcher
->SetExtraRequestHeaders(headers
);
631 fetcher
->SaveResponseWithWriter(scoped_ptr
<net::URLFetcherResponseWriter
>(
632 new ResponseWriter(weak_factory_
.GetWeakPtr(), stream_id
)));
636 void DevToolsUIBindings::OpenInNewTab(const std::string
& url
) {
637 delegate_
->OpenInNewTab(url
);
640 void DevToolsUIBindings::SaveToFile(const std::string
& url
,
641 const std::string
& content
,
643 file_helper_
->Save(url
, content
, save_as
,
644 base::Bind(&DevToolsUIBindings::FileSavedAs
,
645 weak_factory_
.GetWeakPtr(), url
),
646 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs
,
647 weak_factory_
.GetWeakPtr(), url
));
650 void DevToolsUIBindings::AppendToFile(const std::string
& url
,
651 const std::string
& content
) {
652 file_helper_
->Append(url
, content
,
653 base::Bind(&DevToolsUIBindings::AppendedTo
,
654 weak_factory_
.GetWeakPtr(), url
));
657 void DevToolsUIBindings::RequestFileSystems() {
658 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
659 file_helper_
->RequestFileSystems(base::Bind(
660 &DevToolsUIBindings::FileSystemsLoaded
, weak_factory_
.GetWeakPtr()));
663 void DevToolsUIBindings::AddFileSystem() {
664 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
665 file_helper_
->AddFileSystem(
666 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
667 weak_factory_
.GetWeakPtr()),
668 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
669 weak_factory_
.GetWeakPtr()));
672 void DevToolsUIBindings::RemoveFileSystem(const std::string
& file_system_path
) {
673 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
674 file_helper_
->RemoveFileSystem(file_system_path
);
675 base::StringValue
file_system_path_value(file_system_path
);
676 CallClientFunction("DevToolsAPI.fileSystemRemoved",
677 &file_system_path_value
, NULL
, NULL
);
680 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
681 const std::string
& file_system_url
) {
682 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
683 file_helper_
->UpgradeDraggedFileSystemPermissions(
685 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
686 weak_factory_
.GetWeakPtr()),
687 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
688 weak_factory_
.GetWeakPtr()));
691 void DevToolsUIBindings::IndexPath(int index_request_id
,
692 const std::string
& file_system_path
) {
693 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
694 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
695 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
696 IndexingDone(index_request_id
, file_system_path
);
699 if (indexing_jobs_
.count(index_request_id
) != 0)
701 indexing_jobs_
[index_request_id
] =
702 scoped_refptr
<DevToolsFileSystemIndexer::FileSystemIndexingJob
>(
703 file_system_indexer_
->IndexPath(
705 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated
,
706 weak_factory_
.GetWeakPtr(),
709 Bind(&DevToolsUIBindings::IndexingWorked
,
710 weak_factory_
.GetWeakPtr(),
713 Bind(&DevToolsUIBindings::IndexingDone
,
714 weak_factory_
.GetWeakPtr(),
719 void DevToolsUIBindings::StopIndexing(int index_request_id
) {
720 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
721 IndexingJobsMap::iterator it
= indexing_jobs_
.find(index_request_id
);
722 if (it
== indexing_jobs_
.end())
725 indexing_jobs_
.erase(it
);
728 void DevToolsUIBindings::SearchInPath(int search_request_id
,
729 const std::string
& file_system_path
,
730 const std::string
& query
) {
731 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
732 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
733 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
734 SearchCompleted(search_request_id
,
736 std::vector
<std::string
>());
739 file_system_indexer_
->SearchInPath(file_system_path
,
741 Bind(&DevToolsUIBindings::SearchCompleted
,
742 weak_factory_
.GetWeakPtr(),
747 void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string
& message
) {
748 delegate_
->SetWhitelistedShortcuts(message
);
751 void DevToolsUIBindings::ZoomIn() {
752 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN
);
755 void DevToolsUIBindings::ZoomOut() {
756 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT
);
759 void DevToolsUIBindings::ResetZoom() {
760 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET
);
763 void DevToolsUIBindings::SetDevicesDiscoveryConfig(
764 bool discover_usb_devices
,
765 bool port_forwarding_enabled
,
766 const std::string
& port_forwarding_config
) {
767 base::DictionaryValue
* config_dict
= nullptr;
768 scoped_ptr
<base::Value
> parsed_config
=
769 base::JSONReader::Read(port_forwarding_config
);
770 if (!parsed_config
|| !parsed_config
->GetAsDictionary(&config_dict
))
773 profile_
->GetPrefs()->SetBoolean(
774 prefs::kDevToolsDiscoverUsbDevicesEnabled
, discover_usb_devices
);
775 profile_
->GetPrefs()->SetBoolean(
776 prefs::kDevToolsPortForwardingEnabled
, port_forwarding_enabled
);
777 profile_
->GetPrefs()->Set(
778 prefs::kDevToolsPortForwardingConfig
, *config_dict
);
781 void DevToolsUIBindings::DevicesDiscoveryConfigUpdated() {
783 "DevToolsAPI.devicesDiscoveryConfigChanged",
784 profile_
->GetPrefs()->FindPreference(
785 prefs::kDevToolsDiscoverUsbDevicesEnabled
)->GetValue(),
786 profile_
->GetPrefs()->FindPreference(
787 prefs::kDevToolsPortForwardingEnabled
)->GetValue(),
788 profile_
->GetPrefs()->FindPreference(
789 prefs::kDevToolsPortForwardingConfig
)->GetValue());
792 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled
) {
793 if (devices_updates_enabled_
== enabled
)
795 devices_updates_enabled_
= enabled
;
797 remote_targets_handler_
= DevToolsTargetsUIHandler::CreateForAdb(
798 base::Bind(&DevToolsUIBindings::DevicesUpdated
,
799 base::Unretained(this)),
801 pref_change_registrar_
.Init(profile_
->GetPrefs());
802 pref_change_registrar_
.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled
,
803 base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated
,
804 base::Unretained(this)));
805 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingEnabled
,
806 base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated
,
807 base::Unretained(this)));
808 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingConfig
,
809 base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated
,
810 base::Unretained(this)));
811 DevicesDiscoveryConfigUpdated();
813 remote_targets_handler_
.reset();
814 pref_change_registrar_
.RemoveAll();
818 void DevToolsUIBindings::PerformActionOnRemotePage(const std::string
& page_id
,
819 const std::string
& action
) {
820 if (!remote_targets_handler_
)
822 DevToolsTargetImpl
* target
= remote_targets_handler_
->GetTarget(page_id
);
825 if (action
== kRemotePageActionInspect
)
826 target
->Inspect(profile_
);
827 if (action
== kRemotePageActionReload
)
829 if (action
== kRemotePageActionActivate
)
831 if (action
== kRemotePageActionClose
)
835 void DevToolsUIBindings::GetPreferences(const DispatchCallback
& callback
) {
836 const DictionaryValue
* prefs
=
837 profile_
->GetPrefs()->GetDictionary(prefs::kDevToolsPreferences
);
841 void DevToolsUIBindings::SetPreference(const std::string
& name
,
842 const std::string
& value
) {
843 DictionaryPrefUpdate
update(profile_
->GetPrefs(),
844 prefs::kDevToolsPreferences
);
845 update
.Get()->SetStringWithoutPathExpansion(name
, value
);
848 void DevToolsUIBindings::RemovePreference(const std::string
& name
) {
849 DictionaryPrefUpdate
update(profile_
->GetPrefs(),
850 prefs::kDevToolsPreferences
);
851 update
.Get()->RemoveWithoutPathExpansion(name
, nullptr);
854 void DevToolsUIBindings::ClearPreferences() {
855 DictionaryPrefUpdate
update(profile_
->GetPrefs(),
856 prefs::kDevToolsPreferences
);
857 update
.Get()->Clear();
860 void DevToolsUIBindings::SendMessageToBrowser(const std::string
& message
) {
861 if (agent_host_
.get())
862 agent_host_
->DispatchProtocolMessage(message
);
865 void DevToolsUIBindings::RecordEnumeratedHistogram(const std::string
& name
,
867 int boundary_value
) {
868 if (!(boundary_value
>= 0 && boundary_value
<= 100 && sample
>= 0 &&
869 sample
< boundary_value
)) {
870 // TODO(nick): Replace with chrome::bad_message::ReceivedBadMessage().
871 frontend_host_
->BadMessageRecieved();
874 // Each histogram name must follow a different code path in
875 // order to UMA_HISTOGRAM_ENUMERATION work correctly.
876 if (name
== kDevToolsActionTakenHistogram
)
877 UMA_HISTOGRAM_ENUMERATION(name
, sample
, boundary_value
);
878 else if (name
== kDevToolsPanelShownHistogram
)
879 UMA_HISTOGRAM_ENUMERATION(name
, sample
, boundary_value
);
881 frontend_host_
->BadMessageRecieved();
884 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback
& callback
,
885 const std::string
& browser_id
,
886 const std::string
& url
) {
887 if (!android_bridge_
) {
888 callback
.Run(nullptr);
891 android_bridge_
->SendJsonRequest(browser_id
, url
,
892 base::Bind(&DevToolsUIBindings::JsonReceived
,
893 weak_factory_
.GetWeakPtr(),
897 void DevToolsUIBindings::SendFrontendAPINotification(
898 const std::string
& message
) {
899 if (!open_api_channel_
)
901 open_api_channel_
->DispatchOnClientHost(message
);
904 void DevToolsUIBindings::JsonReceived(const DispatchCallback
& callback
,
906 const std::string
& message
) {
907 if (result
!= net::OK
) {
908 callback
.Run(nullptr);
911 base::StringValue
message_value(message
);
912 callback
.Run(&message_value
);
915 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher
* source
) {
917 PendingRequestsMap::iterator it
= pending_requests_
.find(source
);
918 DCHECK(it
!= pending_requests_
.end());
920 base::DictionaryValue response
;
921 base::DictionaryValue
* headers
= new base::DictionaryValue();
922 net::HttpResponseHeaders
* rh
= source
->GetResponseHeaders();
923 response
.SetInteger("statusCode", rh
? rh
->response_code() : 200);
924 response
.Set("headers", headers
);
926 void* iterator
= NULL
;
929 while (rh
&& rh
->EnumerateHeaderLines(&iterator
, &name
, &value
))
930 headers
->SetString(name
, value
);
932 it
->second
.Run(&response
);
933 pending_requests_
.erase(it
);
937 void DevToolsUIBindings::DeviceCountChanged(int count
) {
938 base::FundamentalValue
value(count
);
939 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value
, NULL
,
943 void DevToolsUIBindings::DevicesUpdated(
944 const std::string
& source
,
945 const base::ListValue
& targets
) {
946 CallClientFunction("DevToolsAPI.devicesUpdated", &targets
, NULL
,
950 void DevToolsUIBindings::FileSavedAs(const std::string
& url
) {
951 base::StringValue
url_value(url
);
952 CallClientFunction("DevToolsAPI.savedURL", &url_value
, NULL
, NULL
);
955 void DevToolsUIBindings::CanceledFileSaveAs(const std::string
& url
) {
956 base::StringValue
url_value(url
);
957 CallClientFunction("DevToolsAPI.canceledSaveURL",
958 &url_value
, NULL
, NULL
);
961 void DevToolsUIBindings::AppendedTo(const std::string
& url
) {
962 base::StringValue
url_value(url
);
963 CallClientFunction("DevToolsAPI.appendedToURL", &url_value
, NULL
,
967 void DevToolsUIBindings::FileSystemsLoaded(
968 const std::vector
<DevToolsFileHelper::FileSystem
>& file_systems
) {
969 base::ListValue file_systems_value
;
970 for (size_t i
= 0; i
< file_systems
.size(); ++i
)
971 file_systems_value
.Append(CreateFileSystemValue(file_systems
[i
]));
972 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
973 &file_systems_value
, NULL
, NULL
);
976 void DevToolsUIBindings::FileSystemAdded(
977 const DevToolsFileHelper::FileSystem
& file_system
) {
978 scoped_ptr
<base::StringValue
> error_string_value(
979 new base::StringValue(std::string()));
980 scoped_ptr
<base::DictionaryValue
> file_system_value
;
981 if (!file_system
.file_system_path
.empty())
982 file_system_value
.reset(CreateFileSystemValue(file_system
));
983 CallClientFunction("DevToolsAPI.fileSystemAdded",
984 error_string_value
.get(), file_system_value
.get(), NULL
);
987 void DevToolsUIBindings::IndexingTotalWorkCalculated(
989 const std::string
& file_system_path
,
991 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
992 base::FundamentalValue
request_id_value(request_id
);
993 base::StringValue
file_system_path_value(file_system_path
);
994 base::FundamentalValue
total_work_value(total_work
);
995 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
996 &request_id_value
, &file_system_path_value
,
1000 void DevToolsUIBindings::IndexingWorked(int request_id
,
1001 const std::string
& file_system_path
,
1003 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1004 base::FundamentalValue
request_id_value(request_id
);
1005 base::StringValue
file_system_path_value(file_system_path
);
1006 base::FundamentalValue
worked_value(worked
);
1007 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value
,
1008 &file_system_path_value
, &worked_value
);
1011 void DevToolsUIBindings::IndexingDone(int request_id
,
1012 const std::string
& file_system_path
) {
1013 indexing_jobs_
.erase(request_id
);
1014 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1015 base::FundamentalValue
request_id_value(request_id
);
1016 base::StringValue
file_system_path_value(file_system_path
);
1017 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value
,
1018 &file_system_path_value
, NULL
);
1021 void DevToolsUIBindings::SearchCompleted(
1023 const std::string
& file_system_path
,
1024 const std::vector
<std::string
>& file_paths
) {
1025 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1026 base::ListValue file_paths_value
;
1027 for (std::vector
<std::string
>::const_iterator
it(file_paths
.begin());
1028 it
!= file_paths
.end(); ++it
) {
1029 file_paths_value
.AppendString(*it
);
1031 base::FundamentalValue
request_id_value(request_id
);
1032 base::StringValue
file_system_path_value(file_system_path
);
1033 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value
,
1034 &file_system_path_value
, &file_paths_value
);
1037 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
1038 const base::string16
& message
,
1039 const InfoBarCallback
& callback
) {
1040 DevToolsConfirmInfoBarDelegate::Create(delegate_
->GetInfoBarService(),
1044 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
1045 const extensions::ExtensionRegistry
* registry
=
1046 extensions::ExtensionRegistry::Get(profile_
->GetOriginalProfile());
1050 base::ListValue results
;
1051 for (const scoped_refptr
<const extensions::Extension
>& extension
:
1052 registry
->enabled_extensions()) {
1053 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension
.get())
1056 base::DictionaryValue
* extension_info
= new base::DictionaryValue();
1057 extension_info
->Set(
1059 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
1060 extension
.get()).spec()));
1061 extension_info
->Set("name", new base::StringValue(extension
->name()));
1062 extension_info
->Set("exposeExperimentalAPIs",
1063 new base::FundamentalValue(
1064 extension
->permissions_data()->HasAPIPermission(
1065 extensions::APIPermission::kExperimental
)));
1066 results
.Append(extension_info
);
1068 CallClientFunction("DevToolsAPI.addExtensions",
1069 &results
, NULL
, NULL
);
1072 void DevToolsUIBindings::SetDelegate(Delegate
* delegate
) {
1073 delegate_
.reset(delegate
);
1076 void DevToolsUIBindings::AttachTo(
1077 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
) {
1078 if (agent_host_
.get())
1080 agent_host_
= agent_host
;
1081 agent_host_
->AttachClient(this);
1084 void DevToolsUIBindings::Reattach() {
1085 DCHECK(agent_host_
.get());
1086 agent_host_
->DetachClient();
1087 agent_host_
->AttachClient(this);
1090 void DevToolsUIBindings::Detach() {
1091 if (agent_host_
.get())
1092 agent_host_
->DetachClient();
1096 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost
* agent_host
) {
1097 return agent_host_
.get() == agent_host
;
1100 content::DevToolsExternalAgentProxyDelegate
*
1101 DevToolsUIBindings::CreateWebSocketAPIChannel() {
1102 if (open_api_channel_
)
1103 open_api_channel_
->ConnectionClosed();
1104 return new WebSocketAPIChannel(weak_factory_
.GetWeakPtr());
1107 void DevToolsUIBindings::CallClientFunction(const std::string
& function_name
,
1108 const base::Value
* arg1
,
1109 const base::Value
* arg2
,
1110 const base::Value
* arg3
) {
1111 std::string javascript
= function_name
+ "(";
1114 base::JSONWriter::Write(*arg1
, &json
);
1115 javascript
.append(json
);
1117 base::JSONWriter::Write(*arg2
, &json
);
1118 javascript
.append(", ").append(json
);
1120 base::JSONWriter::Write(*arg3
, &json
);
1121 javascript
.append(", ").append(json
);
1125 javascript
.append(");");
1126 web_contents_
->GetMainFrame()->ExecuteJavaScript(
1127 base::UTF8ToUTF16(javascript
));
1130 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
1131 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
1132 // arrives before the LoadCompleted event, thus it should not trigger the
1133 // frontend load handling.
1134 #if !defined(DEBUG_DEVTOOLS)
1139 void DevToolsUIBindings::DidNavigateMainFrame() {
1140 frontend_loaded_
= false;
1143 void DevToolsUIBindings::FrontendLoaded() {
1144 if (frontend_loaded_
)
1146 frontend_loaded_
= true;
1148 // Call delegate first - it seeds importants bit of information.
1149 delegate_
->OnLoadCompleted();
1151 AddDevToolsExtensionsToClient();