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 web_contents_(web_contents
),
410 delegate_(new DefaultBindingsDelegate(web_contents_
)),
411 device_count_updates_enabled_(false),
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 SetDeviceCountUpdatesEnabled(0, false);
456 SetDevicesUpdatesEnabled(0, false);
458 // Remove self from global list.
459 DevToolsUIBindingsList
* instances
= g_instances
.Pointer();
460 DevToolsUIBindingsList::iterator
it(
461 std::find(instances
->begin(), instances
->end(), this));
462 DCHECK(it
!= instances
->end());
463 instances
->erase(it
);
466 // content::NotificationObserver overrides ------------------------------------
467 void DevToolsUIBindings::Observe(int type
,
468 const content::NotificationSource
& source
,
469 const content::NotificationDetails
& details
) {
470 DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED
, type
);
474 // content::DevToolsFrontendHost::Delegate implementation ---------------------
475 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
476 const std::string
& message
) {
478 base::ListValue empty_params
;
479 base::ListValue
* params
= &empty_params
;
481 base::DictionaryValue
* dict
= NULL
;
482 scoped_ptr
<base::Value
> parsed_message(base::JSONReader::Read(message
));
483 if (!parsed_message
||
484 !parsed_message
->GetAsDictionary(&dict
) ||
485 !dict
->GetString(kFrontendHostMethod
, &method
) ||
486 (dict
->HasKey(kFrontendHostParams
) &&
487 !dict
->GetList(kFrontendHostParams
, ¶ms
))) {
488 LOG(ERROR
) << "Invalid message was sent to embedder: " << message
;
492 dict
->GetInteger(kFrontendHostId
, &id
);
493 embedder_message_dispatcher_
->Dispatch(id
, method
, params
);
496 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
497 const std::string
& message
) {
498 if (agent_host_
.get())
499 agent_host_
->DispatchProtocolMessage(message
);
502 // content::DevToolsAgentHostClient implementation --------------------------
503 void DevToolsUIBindings::DispatchProtocolMessage(
504 content::DevToolsAgentHost
* agent_host
, const std::string
& message
) {
505 DCHECK(agent_host
== agent_host_
.get());
507 if (message
.length() < kMaxMessageChunkSize
) {
508 base::string16 javascript
= base::UTF8ToUTF16(
509 "DevToolsAPI.dispatchMessage(" + message
+ ");");
510 web_contents_
->GetMainFrame()->ExecuteJavaScript(javascript
);
514 base::FundamentalValue
total_size(static_cast<int>(message
.length()));
515 for (size_t pos
= 0; pos
< message
.length(); pos
+= kMaxMessageChunkSize
) {
516 base::StringValue
message_value(message
.substr(pos
, kMaxMessageChunkSize
));
517 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
518 &message_value
, pos
? NULL
: &total_size
, NULL
);
522 void DevToolsUIBindings::AgentHostClosed(
523 content::DevToolsAgentHost
* agent_host
,
524 bool replaced_with_another_client
) {
525 DCHECK(agent_host
== agent_host_
.get());
527 delegate_
->InspectedContentsClosing();
530 void DevToolsUIBindings::SendMessageAck(int request_id
,
531 const base::Value
* arg
) {
532 base::FundamentalValue
id_value(request_id
);
533 CallClientFunction("DevToolsAPI.embedderMessageAck",
534 &id_value
, arg
, nullptr);
537 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
538 void DevToolsUIBindings::ActivateWindow(int request_id
) {
539 delegate_
->ActivateWindow();
542 void DevToolsUIBindings::CloseWindow(int request_id
) {
543 delegate_
->CloseWindow();
546 void DevToolsUIBindings::LoadCompleted(int request_id
) {
550 void DevToolsUIBindings::SetInspectedPageBounds(int request_id
,
551 const gfx::Rect
& rect
) {
552 delegate_
->SetInspectedPageBounds(rect
);
555 void DevToolsUIBindings::SetIsDocked(int request_id
, bool dock_requested
) {
556 delegate_
->SetIsDocked(dock_requested
);
557 SendMessageAck(request_id
, nullptr);
560 void DevToolsUIBindings::InspectElementCompleted(int request_id
) {
561 delegate_
->InspectElementCompleted();
564 void DevToolsUIBindings::InspectedURLChanged(int request_id
,
565 const std::string
& url
) {
566 content::NavigationController
& controller
= web_contents()->GetController();
567 content::NavigationEntry
* entry
= controller
.GetActiveEntry();
568 // DevTools UI is not localized.
570 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat
, url
.c_str())));
571 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE
);
574 void DevToolsUIBindings::LoadNetworkResource(int request_id
,
575 const std::string
& url
,
576 const std::string
& headers
,
579 if (!gurl
.is_valid()) {
580 base::DictionaryValue response
;
581 response
.SetInteger("statusCode", 404);
582 SendMessageAck(request_id
, &response
);
586 net::URLFetcher
* fetcher
=
587 net::URLFetcher::Create(gurl
, net::URLFetcher::GET
, this);
588 pending_requests_
[fetcher
] = request_id
;
589 fetcher
->SetRequestContext(profile_
->GetRequestContext());
590 fetcher
->SetExtraRequestHeaders(headers
);
591 fetcher
->SaveResponseWithWriter(scoped_ptr
<net::URLFetcherResponseWriter
>(
592 new ResponseWriter(weak_factory_
.GetWeakPtr(), stream_id
)));
596 void DevToolsUIBindings::OpenInNewTab(int request_id
, const std::string
& url
) {
597 delegate_
->OpenInNewTab(url
);
600 void DevToolsUIBindings::SaveToFile(int request_id
,
601 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(int request_id
,
612 const std::string
& url
,
613 const std::string
& content
) {
614 file_helper_
->Append(url
, content
,
615 base::Bind(&DevToolsUIBindings::AppendedTo
,
616 weak_factory_
.GetWeakPtr(), url
));
619 void DevToolsUIBindings::RequestFileSystems(int request_id
) {
620 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
621 file_helper_
->RequestFileSystems(base::Bind(
622 &DevToolsUIBindings::FileSystemsLoaded
, weak_factory_
.GetWeakPtr()));
625 void DevToolsUIBindings::AddFileSystem(int request_id
) {
626 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
627 file_helper_
->AddFileSystem(
628 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
629 weak_factory_
.GetWeakPtr()),
630 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
631 weak_factory_
.GetWeakPtr()));
634 void DevToolsUIBindings::RemoveFileSystem(int request_id
,
635 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(
645 const std::string
& file_system_url
) {
646 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
647 file_helper_
->UpgradeDraggedFileSystemPermissions(
649 base::Bind(&DevToolsUIBindings::FileSystemAdded
,
650 weak_factory_
.GetWeakPtr()),
651 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar
,
652 weak_factory_
.GetWeakPtr()));
655 void DevToolsUIBindings::IndexPath(int request_id
,
656 int index_request_id
,
657 const std::string
& file_system_path
) {
658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
659 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
660 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
661 IndexingDone(index_request_id
, file_system_path
);
664 if (indexing_jobs_
.count(index_request_id
) != 0)
666 indexing_jobs_
[index_request_id
] =
667 scoped_refptr
<DevToolsFileSystemIndexer::FileSystemIndexingJob
>(
668 file_system_indexer_
->IndexPath(
670 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated
,
671 weak_factory_
.GetWeakPtr(),
674 Bind(&DevToolsUIBindings::IndexingWorked
,
675 weak_factory_
.GetWeakPtr(),
678 Bind(&DevToolsUIBindings::IndexingDone
,
679 weak_factory_
.GetWeakPtr(),
684 void DevToolsUIBindings::StopIndexing(int request_id
, int index_request_id
) {
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
686 IndexingJobsMap::iterator it
= indexing_jobs_
.find(index_request_id
);
687 if (it
== indexing_jobs_
.end())
690 indexing_jobs_
.erase(it
);
693 void DevToolsUIBindings::SearchInPath(int request_id
,
694 int search_request_id
,
695 const std::string
& file_system_path
,
696 const std::string
& query
) {
697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
698 CHECK(web_contents_
->GetURL().SchemeIs(content::kChromeDevToolsScheme
));
699 if (!file_helper_
->IsFileSystemAdded(file_system_path
)) {
700 SearchCompleted(search_request_id
,
702 std::vector
<std::string
>());
705 file_system_indexer_
->SearchInPath(file_system_path
,
707 Bind(&DevToolsUIBindings::SearchCompleted
,
708 weak_factory_
.GetWeakPtr(),
713 void DevToolsUIBindings::SetWhitelistedShortcuts(int request_id
,
714 const std::string
& message
) {
715 delegate_
->SetWhitelistedShortcuts(message
);
718 void DevToolsUIBindings::ZoomIn(int request_id
) {
719 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN
);
722 void DevToolsUIBindings::ZoomOut(int request_id
) {
723 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT
);
726 void DevToolsUIBindings::ResetZoom(int request_id
) {
727 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET
);
730 static void InspectTarget(Profile
* profile
, DevToolsTargetImpl
* target
) {
732 target
->Inspect(profile
);
735 void DevToolsUIBindings::OpenUrlOnRemoteDeviceAndInspect(
737 const std::string
& browser_id
,
738 const std::string
& url
) {
739 if (remote_targets_handler_
) {
740 remote_targets_handler_
->Open(browser_id
, url
,
741 base::Bind(&InspectTarget
, profile_
));
745 void DevToolsUIBindings::SetDeviceCountUpdatesEnabled(int request_id
,
747 if (device_count_updates_enabled_
== enabled
)
749 DevToolsAndroidBridge
* adb_bridge
=
750 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
754 device_count_updates_enabled_
= enabled
;
756 adb_bridge
->AddDeviceCountListener(this);
758 adb_bridge
->RemoveDeviceCountListener(this);
761 void DevToolsUIBindings::SetDevicesUpdatesEnabled(int request_id
,
763 if (devices_updates_enabled_
== enabled
)
765 devices_updates_enabled_
= enabled
;
767 remote_targets_handler_
= DevToolsTargetsUIHandler::CreateForAdb(
768 base::Bind(&DevToolsUIBindings::DevicesUpdated
,
769 base::Unretained(this)),
772 remote_targets_handler_
.reset();
776 void DevToolsUIBindings::SendMessageToBrowser(int request_id
,
777 const std::string
& message
) {
778 if (agent_host_
.get())
779 agent_host_
->DispatchProtocolMessage(message
);
782 void DevToolsUIBindings::RecordActionUMA(int request_id
,
783 const std::string
& name
,
785 if (name
== kDevToolsActionTakenHistogram
)
786 UMA_HISTOGRAM_ENUMERATION(name
, action
, kDevToolsActionTakenBoundary
);
787 else if (name
== kDevToolsPanelShownHistogram
)
788 UMA_HISTOGRAM_ENUMERATION(name
, action
, kDevToolsPanelShownBoundary
);
791 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher
* source
) {
793 PendingRequestsMap::iterator it
= pending_requests_
.find(source
);
794 DCHECK(it
!= pending_requests_
.end());
796 base::DictionaryValue response
;
797 base::DictionaryValue
* headers
= new base::DictionaryValue();
798 net::HttpResponseHeaders
* rh
= source
->GetResponseHeaders();
799 response
.SetInteger("statusCode", rh
? rh
->response_code() : 200);
800 response
.Set("headers", headers
);
802 void* iterator
= NULL
;
805 while (rh
&& rh
->EnumerateHeaderLines(&iterator
, &name
, &value
))
806 headers
->SetString(name
, value
);
808 SendMessageAck(it
->second
, &response
);
809 pending_requests_
.erase(it
);
813 void DevToolsUIBindings::DeviceCountChanged(int count
) {
814 base::FundamentalValue
value(count
);
815 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value
, NULL
,
819 void DevToolsUIBindings::DevicesUpdated(
820 const std::string
& source
,
821 const base::ListValue
& targets
) {
822 CallClientFunction("DevToolsAPI.devicesUpdated", &targets
, NULL
,
826 void DevToolsUIBindings::FileSavedAs(const std::string
& url
) {
827 base::StringValue
url_value(url
);
828 CallClientFunction("DevToolsAPI.savedURL", &url_value
, NULL
, NULL
);
831 void DevToolsUIBindings::CanceledFileSaveAs(const std::string
& url
) {
832 base::StringValue
url_value(url
);
833 CallClientFunction("DevToolsAPI.canceledSaveURL",
834 &url_value
, NULL
, NULL
);
837 void DevToolsUIBindings::AppendedTo(const std::string
& url
) {
838 base::StringValue
url_value(url
);
839 CallClientFunction("DevToolsAPI.appendedToURL", &url_value
, NULL
,
843 void DevToolsUIBindings::FileSystemsLoaded(
844 const std::vector
<DevToolsFileHelper::FileSystem
>& file_systems
) {
845 base::ListValue file_systems_value
;
846 for (size_t i
= 0; i
< file_systems
.size(); ++i
)
847 file_systems_value
.Append(CreateFileSystemValue(file_systems
[i
]));
848 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
849 &file_systems_value
, NULL
, NULL
);
852 void DevToolsUIBindings::FileSystemAdded(
853 const DevToolsFileHelper::FileSystem
& file_system
) {
854 scoped_ptr
<base::StringValue
> error_string_value(
855 new base::StringValue(std::string()));
856 scoped_ptr
<base::DictionaryValue
> file_system_value
;
857 if (!file_system
.file_system_path
.empty())
858 file_system_value
.reset(CreateFileSystemValue(file_system
));
859 CallClientFunction("DevToolsAPI.fileSystemAdded",
860 error_string_value
.get(), file_system_value
.get(), NULL
);
863 void DevToolsUIBindings::IndexingTotalWorkCalculated(
865 const std::string
& file_system_path
,
867 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
868 base::FundamentalValue
request_id_value(request_id
);
869 base::StringValue
file_system_path_value(file_system_path
);
870 base::FundamentalValue
total_work_value(total_work
);
871 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
872 &request_id_value
, &file_system_path_value
,
876 void DevToolsUIBindings::IndexingWorked(int request_id
,
877 const std::string
& file_system_path
,
879 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
880 base::FundamentalValue
request_id_value(request_id
);
881 base::StringValue
file_system_path_value(file_system_path
);
882 base::FundamentalValue
worked_value(worked
);
883 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value
,
884 &file_system_path_value
, &worked_value
);
887 void DevToolsUIBindings::IndexingDone(int request_id
,
888 const std::string
& file_system_path
) {
889 indexing_jobs_
.erase(request_id
);
890 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
891 base::FundamentalValue
request_id_value(request_id
);
892 base::StringValue
file_system_path_value(file_system_path
);
893 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value
,
894 &file_system_path_value
, NULL
);
897 void DevToolsUIBindings::SearchCompleted(
899 const std::string
& file_system_path
,
900 const std::vector
<std::string
>& file_paths
) {
901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
902 base::ListValue file_paths_value
;
903 for (std::vector
<std::string
>::const_iterator
it(file_paths
.begin());
904 it
!= file_paths
.end(); ++it
) {
905 file_paths_value
.AppendString(*it
);
907 base::FundamentalValue
request_id_value(request_id
);
908 base::StringValue
file_system_path_value(file_system_path
);
909 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value
,
910 &file_system_path_value
, &file_paths_value
);
913 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
914 const base::string16
& message
,
915 const InfoBarCallback
& callback
) {
916 DevToolsConfirmInfoBarDelegate::Create(delegate_
->GetInfoBarService(),
920 void DevToolsUIBindings::UpdateTheme() {
921 ThemeService
* tp
= ThemeServiceFactory::GetForProfile(profile_
);
924 std::string
command("DevToolsAPI.setToolbarColors(\"" +
925 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_TOOLBAR
)) +
927 SkColorToRGBAString(tp
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
)) +
929 web_contents_
->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(command
));
932 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
933 const extensions::ExtensionRegistry
* registry
=
934 extensions::ExtensionRegistry::Get(profile_
->GetOriginalProfile());
938 base::ListValue results
;
939 for (const scoped_refptr
<const extensions::Extension
>& extension
:
940 registry
->enabled_extensions()) {
941 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension
.get())
944 base::DictionaryValue
* extension_info
= new base::DictionaryValue();
947 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
948 extension
.get()).spec()));
949 extension_info
->Set("name", new base::StringValue(extension
->name()));
950 extension_info
->Set("exposeExperimentalAPIs",
951 new base::FundamentalValue(
952 extension
->permissions_data()->HasAPIPermission(
953 extensions::APIPermission::kExperimental
)));
954 results
.Append(extension_info
);
956 CallClientFunction("DevToolsAPI.addExtensions",
957 &results
, NULL
, NULL
);
960 void DevToolsUIBindings::SetDelegate(Delegate
* delegate
) {
961 delegate_
.reset(delegate
);
964 void DevToolsUIBindings::AttachTo(
965 const scoped_refptr
<content::DevToolsAgentHost
>& agent_host
) {
966 if (agent_host_
.get())
968 agent_host_
= agent_host
;
969 agent_host_
->AttachClient(this);
972 void DevToolsUIBindings::Reattach() {
973 DCHECK(agent_host_
.get());
974 agent_host_
->DetachClient();
975 agent_host_
->AttachClient(this);
978 void DevToolsUIBindings::Detach() {
979 if (agent_host_
.get())
980 agent_host_
->DetachClient();
984 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost
* agent_host
) {
985 return agent_host_
.get() == agent_host
;
988 void DevToolsUIBindings::CallClientFunction(const std::string
& function_name
,
989 const base::Value
* arg1
,
990 const base::Value
* arg2
,
991 const base::Value
* arg3
) {
992 std::string javascript
= function_name
+ "(";
995 base::JSONWriter::Write(arg1
, &json
);
996 javascript
.append(json
);
998 base::JSONWriter::Write(arg2
, &json
);
999 javascript
.append(", ").append(json
);
1001 base::JSONWriter::Write(arg3
, &json
);
1002 javascript
.append(", ").append(json
);
1006 javascript
.append(");");
1007 web_contents_
->GetMainFrame()->ExecuteJavaScript(
1008 base::UTF8ToUTF16(javascript
));
1011 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
1012 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
1013 // arrives before the LoadCompleted event, thus it should not trigger the
1014 // frontend load handling.
1015 #if !defined(DEBUG_DEVTOOLS)
1020 void DevToolsUIBindings::DidNavigateMainFrame() {
1021 frontend_loaded_
= false;
1024 void DevToolsUIBindings::FrontendLoaded() {
1025 if (frontend_loaded_
)
1027 frontend_loaded_
= true;
1029 // Call delegate first - it seeds importants bit of information.
1030 delegate_
->OnLoadCompleted();
1033 AddDevToolsExtensionsToClient();