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 int kDevToolsActionTakenBoundary
= 100;
75 static const char kDevToolsPanelShownHistogram
[] = "DevTools.PanelShown";
76 static const int kDevToolsPanelShownBoundary
= 20;
78 // This constant should be in sync with
79 // the constant at shell_devtools_frontend.cc.
80 const size_t kMaxMessageChunkSize
= IPC::Channel::kMaximumMessageSize
/ 4;
82 typedef std::vector
<DevToolsUIBindings
*> DevToolsUIBindingsList
;
83 base::LazyInstance
<DevToolsUIBindingsList
>::Leaky g_instances
=
84 LAZY_INSTANCE_INITIALIZER
;
86 std::string
SkColorToRGBAString(SkColor color
) {
87 // We avoid StringPrintf because it will use locale specific formatters for
88 // the double (e.g. ',' instead of '.' in German).
89 return "rgba(" + base::IntToString(SkColorGetR(color
)) + "," +
90 base::IntToString(SkColorGetG(color
)) + "," +
91 base::IntToString(SkColorGetB(color
)) + "," +
92 base::DoubleToString(SkColorGetA(color
) / 255.0) + ")";
95 base::DictionaryValue
* CreateFileSystemValue(
96 DevToolsFileHelper::FileSystem file_system
) {
97 base::DictionaryValue
* file_system_value
= new base::DictionaryValue();
98 file_system_value
->SetString("fileSystemName", file_system
.file_system_name
);
99 file_system_value
->SetString("rootURL", file_system
.root_url
);
100 file_system_value
->SetString("fileSystemPath", file_system
.file_system_path
);
101 return file_system_value
;
104 Browser
* FindBrowser(content::WebContents
* web_contents
) {
105 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
106 int tab_index
= it
->tab_strip_model()->GetIndexOfWebContents(
108 if (tab_index
!= TabStripModel::kNoTab
)
114 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
116 typedef base::Callback
<void(bool)> InfoBarCallback
;
118 class DevToolsConfirmInfoBarDelegate
: public ConfirmInfoBarDelegate
{
120 // If |infobar_service| is NULL, runs |callback| with a single argument with
121 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
122 // and adds the infobar to |infobar_service|.
123 static void Create(InfoBarService
* infobar_service
,
124 const InfoBarCallback
& callback
,
125 const base::string16
& message
);
128 DevToolsConfirmInfoBarDelegate(
129 const InfoBarCallback
& callback
,
130 const base::string16
& message
);
131 ~DevToolsConfirmInfoBarDelegate() override
;
133 base::string16
GetMessageText() const override
;
134 base::string16
GetButtonLabel(InfoBarButton button
) const override
;
135 bool Accept() override
;
136 bool Cancel() override
;
138 InfoBarCallback callback_
;
139 const base::string16 message_
;
141 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate
);
144 void DevToolsConfirmInfoBarDelegate::Create(
145 InfoBarService
* infobar_service
,
146 const InfoBarCallback
& callback
,
147 const base::string16
& message
) {
148 if (!infobar_service
) {
153 infobar_service
->AddInfoBar(
154 infobar_service
->CreateConfirmInfoBar(scoped_ptr
<ConfirmInfoBarDelegate
>(
155 new DevToolsConfirmInfoBarDelegate(callback
, message
))));
158 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
159 const InfoBarCallback
& callback
,
160 const base::string16
& message
)
161 : ConfirmInfoBarDelegate(),
166 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
167 if (!callback_
.is_null())
168 callback_
.Run(false);
171 base::string16
DevToolsConfirmInfoBarDelegate::GetMessageText() const {
175 base::string16
DevToolsConfirmInfoBarDelegate::GetButtonLabel(
176 InfoBarButton button
) const {
177 return l10n_util::GetStringUTF16((button
== BUTTON_OK
) ?
178 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON
: IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON
);
181 bool DevToolsConfirmInfoBarDelegate::Accept() {
187 bool DevToolsConfirmInfoBarDelegate::Cancel() {
188 callback_
.Run(false);
193 // DevToolsUIDefaultDelegate --------------------------------------------------
195 class DefaultBindingsDelegate
: public DevToolsUIBindings::Delegate
{
197 explicit DefaultBindingsDelegate(content::WebContents
* web_contents
)
198 : web_contents_(web_contents
) {}
201 ~DefaultBindingsDelegate() override
{}
203 void ActivateWindow() override
;
204 void CloseWindow() override
{}
205 void SetInspectedPageBounds(const gfx::Rect
& rect
) override
{}
206 void InspectElementCompleted() override
{}
207 void SetIsDocked(bool is_docked
) override
{}
208 void OpenInNewTab(const std::string
& url
) override
;
209 void SetWhitelistedShortcuts(const std::string
& message
) override
{}
211 void InspectedContentsClosing() override
;
212 void OnLoadCompleted() override
{}
213 InfoBarService
* GetInfoBarService() override
;
214 void RenderProcessGone(bool crashed
) override
{}
216 content::WebContents
* web_contents_
;
217 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate
);
220 void DefaultBindingsDelegate::ActivateWindow() {
221 web_contents_
->GetDelegate()->ActivateContents(web_contents_
);
222 web_contents_
->Focus();
225 void DefaultBindingsDelegate::OpenInNewTab(const std::string
& url
) {
226 content::OpenURLParams
params(
227 GURL(url
), content::Referrer(), NEW_FOREGROUND_TAB
,
228 ui::PAGE_TRANSITION_LINK
, false);
229 Browser
* browser
= FindBrowser(web_contents_
);
230 browser
->OpenURL(params
);
233 void DefaultBindingsDelegate::InspectedContentsClosing() {
234 web_contents_
->GetRenderViewHost()->ClosePage();
237 InfoBarService
* DefaultBindingsDelegate::GetInfoBarService() {
238 return InfoBarService::FromWebContents(web_contents_
);
241 // ResponseWriter -------------------------------------------------------------
243 class ResponseWriter
: public net::URLFetcherResponseWriter
{
245 ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
, int stream_id
);
246 ~ResponseWriter() override
;
248 // URLFetcherResponseWriter overrides:
249 int Initialize(const net::CompletionCallback
& callback
) override
;
250 int Write(net::IOBuffer
* buffer
,
252 const net::CompletionCallback
& callback
) override
;
253 int Finish(const net::CompletionCallback
& callback
) override
;
256 base::WeakPtr
<DevToolsUIBindings
> bindings_
;
259 DISALLOW_COPY_AND_ASSIGN(ResponseWriter
);
262 ResponseWriter::ResponseWriter(base::WeakPtr
<DevToolsUIBindings
> bindings
,
264 : bindings_(bindings
),
265 stream_id_(stream_id
) {
268 ResponseWriter::~ResponseWriter() {
271 int ResponseWriter::Initialize(const net::CompletionCallback
& callback
) {
275 int ResponseWriter::Write(net::IOBuffer
* buffer
,
277 const net::CompletionCallback
& callback
) {
278 base::FundamentalValue
* id
= new base::FundamentalValue(stream_id_
);
279 base::StringValue
* chunk
=
280 new base::StringValue(std::string(buffer
->data(), num_bytes
));
282 content::BrowserThread::PostTask(
283 content::BrowserThread::UI
, FROM_HERE
,
284 base::Bind(&DevToolsUIBindings::CallClientFunction
,
285 bindings_
, "DevToolsAPI.streamWrite",
286 base::Owned(id
), base::Owned(chunk
), nullptr));
290 int ResponseWriter::Finish(const net::CompletionCallback
& callback
) {
296 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
298 class DevToolsUIBindings::FrontendWebContentsObserver
299 : public content::WebContentsObserver
{
301 explicit FrontendWebContentsObserver(DevToolsUIBindings
* ui_bindings
);
302 ~FrontendWebContentsObserver() override
;
305 // contents::WebContentsObserver:
306 void RenderProcessGone(base::TerminationStatus status
) override
;
307 // TODO(creis): Replace with RenderFrameCreated when http://crbug.com/425397
308 // is fixed. See also http://crbug.com/424641.
309 void AboutToNavigateRenderFrame(
310 content::RenderFrameHost
* old_host
,
311 content::RenderFrameHost
* new_host
) override
;
312 void DocumentOnLoadCompletedInMainFrame() override
;
313 void DidNavigateMainFrame(
314 const content::LoadCommittedDetails
& details
,
315 const content::FrameNavigateParams
& params
) override
;
317 DevToolsUIBindings
* devtools_bindings_
;
318 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver
);
321 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
322 DevToolsUIBindings
* devtools_ui_bindings
)
323 : WebContentsObserver(devtools_ui_bindings
->web_contents()),
324 devtools_bindings_(devtools_ui_bindings
) {
327 DevToolsUIBindings::FrontendWebContentsObserver::
328 ~FrontendWebContentsObserver() {
331 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
332 base::TerminationStatus status
) {
335 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
336 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
337 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
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 // DevToolsUIBindings ---------------------------------------------------------
371 DevToolsUIBindings
* DevToolsUIBindings::ForWebContents(
372 content::WebContents
* web_contents
) {
373 if (g_instances
== NULL
)
375 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
376 for (DevToolsUIBindingsList::iterator
it(instances
->begin());
377 it
!= instances
->end(); ++it
) {
378 if ((*it
)->web_contents() == web_contents
)
385 GURL
DevToolsUIBindings::ApplyThemeToURL(Profile
* profile
,
386 const GURL
& base_url
) {
387 std::string frontend_url
= base_url
.spec();
388 ThemeService
* tp
= ThemeServiceFactory::GetForProfile(profile
);
390 std::string
url_string(
392 ((frontend_url
.find("?") == std::string::npos
) ? "?" : "&") +
393 "dockSide=undocked" + // TODO(dgozman): remove this support in M38.
395 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
)) +
397 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
)));
398 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
399 switches::kEnableDevToolsExperiments
))
400 url_string
+= "&experiments=true";
401 #if defined(DEBUG_DEVTOOLS)
402 url_string
+= "&debugFrontend=true";
403 #endif // defined(DEBUG_DEVTOOLS)
404 return GURL(url_string
);
407 DevToolsUIBindings::DevToolsUIBindings(content::WebContents
* web_contents
)
408 : profile_(Profile::FromBrowserContext(web_contents
->GetBrowserContext())),
409 android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_
)),
410 web_contents_(web_contents
),
411 delegate_(new DefaultBindingsDelegate(web_contents_
)),
412 devices_updates_enabled_(false),
413 frontend_loaded_(false),
414 weak_factory_(this) {
415 g_instances
.Get().push_back(this);
416 frontend_contents_observer_
.reset(new FrontendWebContentsObserver(this));
417 web_contents_
->GetMutableRendererPrefs()->can_accept_load_drops
= false;
419 file_helper_
.reset(new DevToolsFileHelper(web_contents_
, profile_
));
420 file_system_indexer_
= new DevToolsFileSystemIndexer();
421 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
424 // Wipe out page icon so that the default application icon is used.
425 content::NavigationEntry
* entry
=
426 web_contents_
->GetController().GetActiveEntry();
427 entry
->GetFavicon().image
= gfx::Image();
428 entry
->GetFavicon().valid
= true;
430 // Register on-load actions.
432 this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
433 content::Source
<ThemeService
>(
434 ThemeServiceFactory::GetForProfile(profile_
)));
436 embedder_message_dispatcher_
.reset(
437 DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
439 frontend_host_
.reset(content::DevToolsFrontendHost::Create(
440 web_contents_
->GetMainFrame(), this));
443 DevToolsUIBindings::~DevToolsUIBindings() {
444 for (const auto& pair
: pending_requests_
)
447 if (agent_host_
.get())
448 agent_host_
->DetachClient();
450 for (IndexingJobsMap::const_iterator
jobs_it(indexing_jobs_
.begin());
451 jobs_it
!= indexing_jobs_
.end(); ++jobs_it
) {
452 jobs_it
->second
->Stop();
454 indexing_jobs_
.clear();
455 SetDevicesUpdatesEnabled(false);
457 // Remove self from global list.
458 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
459 DevToolsUIBindingsList::iterator
it(
460 std::find(instances
->begin(), instances
->end(), this));
461 DCHECK(it
!= instances
->end());
462 instances
->erase(it
);
465 // content::NotificationObserver overrides ------------------------------------
466 void DevToolsUIBindings::Observe(int type
,
467 const content::NotificationSource
& source
,
468 const content::NotificationDetails
& details
) {
469 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED
, type
);
473 // content::DevToolsFrontendHost::Delegate implementation ---------------------
474 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
475 const std::string
& message
) {
477 base::ListValue empty_params
;
478 base::ListValue
* params
= &empty_params
;
480 base::DictionaryValue
* dict
= NULL
;
481 scoped_ptr
<base::Value
> parsed_message(base::JSONReader::Read(message
));
482 if (!parsed_message
||
483 !parsed_message
->GetAsDictionary(&dict
) ||
484 !dict
->GetString(kFrontendHostMethod
, &method
) ||
485 (dict
->HasKey(kFrontendHostParams
) &&
486 !dict
->GetList(kFrontendHostParams
, ¶ms
))) {
487 LOG(ERROR
) << "Invalid message was sent to embedder: " << message
;
491 dict
->GetInteger(kFrontendHostId
, &id
);
492 embedder_message_dispatcher_
->Dispatch(
493 base::Bind(&DevToolsUIBindings::SendMessageAck
,
494 weak_factory_
.GetWeakPtr(),
500 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
501 const std::string
& message
) {
502 if (agent_host_
.get())
503 agent_host_
->DispatchProtocolMessage(message
);
506 // content::DevToolsAgentHostClient implementation --------------------------
507 void DevToolsUIBindings::DispatchProtocolMessage(
508 content::DevToolsAgentHost
* agent_host
, const std::string
& message
) {
509 DCHECK(agent_host
== agent_host_
.get());
511 if (message
.length() < kMaxMessageChunkSize
) {
512 base::string16 javascript
= base::UTF8ToUTF16(
513 "DevToolsAPI.dispatchMessage(" + message
+ ");");
514 web_contents_
->GetMainFrame()->ExecuteJavaScript(javascript
);
518 base::FundamentalValue
total_size(static_cast<int>(message
.length()));
519 for (size_t pos
= 0; pos
< message
.length(); pos
+= kMaxMessageChunkSize
) {
520 base::StringValue
message_value(message
.substr(pos
, kMaxMessageChunkSize
));
521 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
522 &message_value
, pos
? NULL
: &total_size
, NULL
);
526 void DevToolsUIBindings::AgentHostClosed(
527 content::DevToolsAgentHost
* agent_host
,
528 bool replaced_with_another_client
) {
529 DCHECK(agent_host
== agent_host_
.get());
531 delegate_
->InspectedContentsClosing();
534 void DevToolsUIBindings::SendMessageAck(int request_id
,
535 const base::Value
* arg
) {
536 base::FundamentalValue
id_value(request_id
);
537 CallClientFunction("DevToolsAPI.embedderMessageAck",
538 &id_value
, arg
, nullptr);
541 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
542 void DevToolsUIBindings::ActivateWindow() {
543 delegate_
->ActivateWindow();
546 void DevToolsUIBindings::CloseWindow() {
547 delegate_
->CloseWindow();
550 void DevToolsUIBindings::LoadCompleted() {
554 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect
& rect
) {
555 delegate_
->SetInspectedPageBounds(rect
);
558 void DevToolsUIBindings::SetIsDocked(const DispatchCallback
& callback
,
559 bool dock_requested
) {
560 delegate_
->SetIsDocked(dock_requested
);
561 callback
.Run(nullptr);
564 void DevToolsUIBindings::InspectElementCompleted() {
565 delegate_
->InspectElementCompleted();
568 void DevToolsUIBindings::InspectedURLChanged(const std::string
& url
) {
569 content::NavigationController
& controller
= web_contents()->GetController();
570 content::NavigationEntry
* entry
= controller
.GetActiveEntry();
571 // DevTools UI is not localized.
573 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat
, url
.c_str())));
574 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE
);
577 void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback
& callback
,
578 const std::string
& url
,
579 const std::string
& headers
,
582 if (!gurl
.is_valid()) {
583 base::DictionaryValue response
;
584 response
.SetInteger("statusCode", 404);
585 callback
.Run(&response
);
589 net::URLFetcher
* fetcher
=
590 net::URLFetcher::Create(gurl
, net::URLFetcher::GET
, this);
591 pending_requests_
[fetcher
] = callback
;
592 fetcher
->SetRequestContext(profile_
->GetRequestContext());
593 fetcher
->SetExtraRequestHeaders(headers
);
594 fetcher
->SaveResponseWithWriter(scoped_ptr
<net::URLFetcherResponseWriter
>(
595 new ResponseWriter(weak_factory_
.GetWeakPtr(), stream_id
)));
599 void DevToolsUIBindings::OpenInNewTab(const std::string
& url
) {
600 delegate_
->OpenInNewTab(url
);
603 void DevToolsUIBindings::SaveToFile(const std::string
& url
,
604 const std::string
& content
,
606 file_helper_
->Save(url
, content
, save_as
,
607 base::Bind(&DevToolsUIBindings::FileSavedAs
,
608 weak_factory_
.GetWeakPtr(), url
),
609 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs
,
610 weak_factory_
.GetWeakPtr(), url
));
613 void DevToolsUIBindings::AppendToFile(const std::string
& url
,
614 const std::string
& content
) {
615 file_helper_
->Append(url
, content
,
616 base::Bind(&DevToolsUIBindings::AppendedTo
,
617 weak_factory_
.GetWeakPtr(), url
));
620 void DevToolsUIBindings::RequestFileSystems() {
621 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
622 file_helper_
->RequestFileSystems(base::Bind(
623 &DevToolsUIBindings::FileSystemsLoaded
, weak_factory_
.GetWeakPtr()));
626 void DevToolsUIBindings::AddFileSystem() {
627 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
628 file_helper_
->AddFileSystem(
629 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
630 weak_factory_
.GetWeakPtr()),
631 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
632 weak_factory_
.GetWeakPtr()));
635 void DevToolsUIBindings::RemoveFileSystem(const std::string
& file_system_path
) {
636 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
637 file_helper_
->RemoveFileSystem(file_system_path
);
638 base::StringValue
file_system_path_value(file_system_path
);
639 CallClientFunction("DevToolsAPI.fileSystemRemoved",
640 &file_system_path_value
, NULL
, NULL
);
643 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
644 const std::string
& file_system_url
) {
645 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
646 file_helper_
->UpgradeDraggedFileSystemPermissions(
648 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
649 weak_factory_
.GetWeakPtr()),
650 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
651 weak_factory_
.GetWeakPtr()));
654 void DevToolsUIBindings::IndexPath(int index_request_id
,
655 const std::string
& file_system_path
) {
656 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
657 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
658 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
659 IndexingDone(index_request_id
, file_system_path
);
662 if (indexing_jobs_
.count(index_request_id
) != 0)
664 indexing_jobs_
[index_request_id
] =
665 scoped_refptr
<DevToolsFileSystemIndexer::FileSystemIndexingJob
>(
666 file_system_indexer_
->IndexPath(
668 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated
,
669 weak_factory_
.GetWeakPtr(),
672 Bind(&DevToolsUIBindings::IndexingWorked
,
673 weak_factory_
.GetWeakPtr(),
676 Bind(&DevToolsUIBindings::IndexingDone
,
677 weak_factory_
.GetWeakPtr(),
682 void DevToolsUIBindings::StopIndexing(int index_request_id
) {
683 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
684 IndexingJobsMap::iterator it
= indexing_jobs_
.find(index_request_id
);
685 if (it
== indexing_jobs_
.end())
688 indexing_jobs_
.erase(it
);
691 void DevToolsUIBindings::SearchInPath(int search_request_id
,
692 const std::string
& file_system_path
,
693 const std::string
& query
) {
694 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
695 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
696 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
697 SearchCompleted(search_request_id
,
699 std::vector
<std::string
>());
702 file_system_indexer_
->SearchInPath(file_system_path
,
704 Bind(&DevToolsUIBindings::SearchCompleted
,
705 weak_factory_
.GetWeakPtr(),
710 void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string
& message
) {
711 delegate_
->SetWhitelistedShortcuts(message
);
714 void DevToolsUIBindings::ZoomIn() {
715 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN
);
718 void DevToolsUIBindings::ZoomOut() {
719 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT
);
722 void DevToolsUIBindings::ResetZoom() {
723 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET
);
726 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled
) {
727 if (devices_updates_enabled_
== enabled
)
729 devices_updates_enabled_
= enabled
;
731 remote_targets_handler_
= DevToolsTargetsUIHandler::CreateForAdb(
732 base::Bind(&DevToolsUIBindings::DevicesUpdated
,
733 base::Unretained(this)),
736 remote_targets_handler_
.reset();
740 void DevToolsUIBindings::SendMessageToBrowser(const std::string
& message
) {
741 if (agent_host_
.get())
742 agent_host_
->DispatchProtocolMessage(message
);
745 void DevToolsUIBindings::RecordActionUMA(const std::string
& name
, int action
) {
746 if (name
== kDevToolsActionTakenHistogram
)
747 UMA_HISTOGRAM_ENUMERATION(name
, action
, kDevToolsActionTakenBoundary
);
748 else if (name
== kDevToolsPanelShownHistogram
)
749 UMA_HISTOGRAM_ENUMERATION(name
, action
, kDevToolsPanelShownBoundary
);
752 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback
& callback
,
753 const std::string
& browser_id
,
754 const std::string
& url
) {
755 if (!android_bridge_
) {
756 callback
.Run(nullptr);
759 android_bridge_
->SendJsonRequest(browser_id
, url
,
760 base::Bind(&DevToolsUIBindings::JsonReceived
,
761 weak_factory_
.GetWeakPtr(),
765 void DevToolsUIBindings::JsonReceived(const DispatchCallback
& callback
,
767 const std::string
& message
) {
768 if (result
!= net::OK
) {
769 callback
.Run(nullptr);
772 base::StringValue
message_value(message
);
773 callback
.Run(&message_value
);
776 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher
* source
) {
778 PendingRequestsMap::iterator it
= pending_requests_
.find(source
);
779 DCHECK(it
!= pending_requests_
.end());
781 base::DictionaryValue response
;
782 base::DictionaryValue
* headers
= new base::DictionaryValue();
783 net::HttpResponseHeaders
* rh
= source
->GetResponseHeaders();
784 response
.SetInteger("statusCode", rh
? rh
->response_code() : 200);
785 response
.Set("headers", headers
);
787 void* iterator
= NULL
;
790 while (rh
&& rh
->EnumerateHeaderLines(&iterator
, &name
, &value
))
791 headers
->SetString(name
, value
);
793 it
->second
.Run(&response
);
794 pending_requests_
.erase(it
);
798 void DevToolsUIBindings::DeviceCountChanged(int count
) {
799 base::FundamentalValue
value(count
);
800 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value
, NULL
,
804 void DevToolsUIBindings::DevicesUpdated(
805 const std::string
& source
,
806 const base::ListValue
& targets
) {
807 CallClientFunction("DevToolsAPI.devicesUpdated", &targets
, NULL
,
811 void DevToolsUIBindings::FileSavedAs(const std::string
& url
) {
812 base::StringValue
url_value(url
);
813 CallClientFunction("DevToolsAPI.savedURL", &url_value
, NULL
, NULL
);
816 void DevToolsUIBindings::CanceledFileSaveAs(const std::string
& url
) {
817 base::StringValue
url_value(url
);
818 CallClientFunction("DevToolsAPI.canceledSaveURL",
819 &url_value
, NULL
, NULL
);
822 void DevToolsUIBindings::AppendedTo(const std::string
& url
) {
823 base::StringValue
url_value(url
);
824 CallClientFunction("DevToolsAPI.appendedToURL", &url_value
, NULL
,
828 void DevToolsUIBindings::FileSystemsLoaded(
829 const std::vector
<DevToolsFileHelper::FileSystem
>& file_systems
) {
830 base::ListValue file_systems_value
;
831 for (size_t i
= 0; i
< file_systems
.size(); ++i
)
832 file_systems_value
.Append(CreateFileSystemValue(file_systems
[i
]));
833 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
834 &file_systems_value
, NULL
, NULL
);
837 void DevToolsUIBindings::FileSystemAdded(
838 const DevToolsFileHelper::FileSystem
& file_system
) {
839 scoped_ptr
<base::StringValue
> error_string_value(
840 new base::StringValue(std::string()));
841 scoped_ptr
<base::DictionaryValue
> file_system_value
;
842 if (!file_system
.file_system_path
.empty())
843 file_system_value
.reset(CreateFileSystemValue(file_system
));
844 CallClientFunction("DevToolsAPI.fileSystemAdded",
845 error_string_value
.get(), file_system_value
.get(), NULL
);
848 void DevToolsUIBindings::IndexingTotalWorkCalculated(
850 const std::string
& file_system_path
,
852 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
853 base::FundamentalValue
request_id_value(request_id
);
854 base::StringValue
file_system_path_value(file_system_path
);
855 base::FundamentalValue
total_work_value(total_work
);
856 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
857 &request_id_value
, &file_system_path_value
,
861 void DevToolsUIBindings::IndexingWorked(int request_id
,
862 const std::string
& file_system_path
,
864 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
865 base::FundamentalValue
request_id_value(request_id
);
866 base::StringValue
file_system_path_value(file_system_path
);
867 base::FundamentalValue
worked_value(worked
);
868 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value
,
869 &file_system_path_value
, &worked_value
);
872 void DevToolsUIBindings::IndexingDone(int request_id
,
873 const std::string
& file_system_path
) {
874 indexing_jobs_
.erase(request_id
);
875 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
876 base::FundamentalValue
request_id_value(request_id
);
877 base::StringValue
file_system_path_value(file_system_path
);
878 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value
,
879 &file_system_path_value
, NULL
);
882 void DevToolsUIBindings::SearchCompleted(
884 const std::string
& file_system_path
,
885 const std::vector
<std::string
>& file_paths
) {
886 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
887 base::ListValue file_paths_value
;
888 for (std::vector
<std::string
>::const_iterator
it(file_paths
.begin());
889 it
!= file_paths
.end(); ++it
) {
890 file_paths_value
.AppendString(*it
);
892 base::FundamentalValue
request_id_value(request_id
);
893 base::StringValue
file_system_path_value(file_system_path
);
894 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value
,
895 &file_system_path_value
, &file_paths_value
);
898 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
899 const base::string16
& message
,
900 const InfoBarCallback
& callback
) {
901 DevToolsConfirmInfoBarDelegate::Create(delegate_
->GetInfoBarService(),
905 void DevToolsUIBindings::UpdateTheme() {
906 ThemeService
* tp
= ThemeServiceFactory::GetForProfile(profile_
);
909 std::string
command("DevToolsAPI.setToolbarColors(\"" +
910 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
)) +
912 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
)) +
914 web_contents_
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(command
));
917 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
918 const extensions::ExtensionRegistry
* registry
=
919 extensions::ExtensionRegistry::Get(profile_
->GetOriginalProfile());
923 base::ListValue results
;
924 for (const scoped_refptr
<const extensions::Extension
>& extension
:
925 registry
->enabled_extensions()) {
926 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension
.get())
929 base::DictionaryValue
* extension_info
= new base::DictionaryValue();
932 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
933 extension
.get()).spec()));
934 extension_info
->Set("name", new base::StringValue(extension
->name()));
935 extension_info
->Set("exposeExperimentalAPIs",
936 new base::FundamentalValue(
937 extension
->permissions_data()->HasAPIPermission(
938 extensions::APIPermission::kExperimental
)));
939 results
.Append(extension_info
);
941 CallClientFunction("DevToolsAPI.addExtensions",
942 &results
, NULL
, NULL
);
945 void DevToolsUIBindings::SetDelegate(Delegate
* delegate
) {
946 delegate_
.reset(delegate
);
949 void DevToolsUIBindings::AttachTo(
950 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
) {
951 if (agent_host_
.get())
953 agent_host_
= agent_host
;
954 agent_host_
->AttachClient(this);
957 void DevToolsUIBindings::Reattach() {
958 DCHECK(agent_host_
.get());
959 agent_host_
->DetachClient();
960 agent_host_
->AttachClient(this);
963 void DevToolsUIBindings::Detach() {
964 if (agent_host_
.get())
965 agent_host_
->DetachClient();
969 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost
* agent_host
) {
970 return agent_host_
.get() == agent_host
;
973 void DevToolsUIBindings::CallClientFunction(const std::string
& function_name
,
974 const base::Value
* arg1
,
975 const base::Value
* arg2
,
976 const base::Value
* arg3
) {
977 std::string javascript
= function_name
+ "(";
980 base::JSONWriter::Write(arg1
, &json
);
981 javascript
.append(json
);
983 base::JSONWriter::Write(arg2
, &json
);
984 javascript
.append(", ").append(json
);
986 base::JSONWriter::Write(arg3
, &json
);
987 javascript
.append(", ").append(json
);
991 javascript
.append(");");
992 web_contents_
->GetMainFrame()->ExecuteJavaScript(
993 base::UTF8ToUTF16(javascript
));
996 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
997 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
998 // arrives before the LoadCompleted event, thus it should not trigger the
999 // frontend load handling.
1000 #if !defined(DEBUG_DEVTOOLS)
1005 void DevToolsUIBindings::DidNavigateMainFrame() {
1006 frontend_loaded_
= false;
1009 void DevToolsUIBindings::FrontendLoaded() {
1010 if (frontend_loaded_
)
1012 frontend_loaded_
= true;
1014 // Call delegate first - it seeds importants bit of information.
1015 delegate_
->OnLoadCompleted();
1018 AddDevToolsExtensionsToClient();