Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_ui_bindings.cc
blob664627cda0ad1de2c52a65d750db6dccfc1c2bb9
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/prefs/pref_service_syncable.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_iterator.h"
24 #include "chrome/browser/ui/browser_list.h"
25 #include "chrome/browser/ui/browser_window.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/common/url_constants.h"
31 #include "chrome/grit/generated_resources.h"
32 #include "components/infobars/core/confirm_infobar_delegate.h"
33 #include "components/infobars/core/infobar.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;
61 namespace content {
62 struct LoadCommittedDetails;
63 struct FrameNavigateParams;
66 namespace {
68 static const char kFrontendHostId[] = "id";
69 static const char kFrontendHostMethod[] = "method";
70 static const char kFrontendHostParams[] = "params";
71 static const char kTitleFormat[] = "Developer Tools - %s";
73 static const char kDevToolsActionTakenHistogram[] = "DevTools.ActionTaken";
74 static const char kDevToolsPanelShownHistogram[] = "DevTools.PanelShown";
76 // This constant should be in sync with
77 // the constant at shell_devtools_frontend.cc.
78 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
80 typedef std::vector<DevToolsUIBindings*> DevToolsUIBindingsList;
81 base::LazyInstance<DevToolsUIBindingsList>::Leaky g_instances =
82 LAZY_INSTANCE_INITIALIZER;
84 base::DictionaryValue* CreateFileSystemValue(
85 DevToolsFileHelper::FileSystem file_system) {
86 base::DictionaryValue* file_system_value = new base::DictionaryValue();
87 file_system_value->SetString("fileSystemName", file_system.file_system_name);
88 file_system_value->SetString("rootURL", file_system.root_url);
89 file_system_value->SetString("fileSystemPath", file_system.file_system_path);
90 return file_system_value;
93 Browser* FindBrowser(content::WebContents* web_contents) {
94 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
95 int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
96 web_contents);
97 if (tab_index != TabStripModel::kNoTab)
98 return *it;
100 return NULL;
103 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
105 typedef base::Callback<void(bool)> InfoBarCallback;
107 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
108 public:
109 // If |infobar_service| is NULL, runs |callback| with a single argument with
110 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
111 // and adds the infobar to |infobar_service|.
112 static void Create(InfoBarService* infobar_service,
113 const InfoBarCallback& callback,
114 const base::string16& message);
116 private:
117 DevToolsConfirmInfoBarDelegate(
118 const InfoBarCallback& callback,
119 const base::string16& message);
120 ~DevToolsConfirmInfoBarDelegate() override;
122 base::string16 GetMessageText() const override;
123 base::string16 GetButtonLabel(InfoBarButton button) const override;
124 bool Accept() override;
125 bool Cancel() override;
127 InfoBarCallback callback_;
128 const base::string16 message_;
130 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
133 void DevToolsConfirmInfoBarDelegate::Create(
134 InfoBarService* infobar_service,
135 const InfoBarCallback& callback,
136 const base::string16& message) {
137 if (!infobar_service) {
138 callback.Run(false);
139 return;
142 infobar_service->AddInfoBar(
143 infobar_service->CreateConfirmInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
144 new DevToolsConfirmInfoBarDelegate(callback, message))));
147 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
148 const InfoBarCallback& callback,
149 const base::string16& message)
150 : ConfirmInfoBarDelegate(),
151 callback_(callback),
152 message_(message) {
155 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
156 if (!callback_.is_null())
157 callback_.Run(false);
160 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
161 return message_;
164 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
165 InfoBarButton button) const {
166 return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
167 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
170 bool DevToolsConfirmInfoBarDelegate::Accept() {
171 callback_.Run(true);
172 callback_.Reset();
173 return true;
176 bool DevToolsConfirmInfoBarDelegate::Cancel() {
177 callback_.Run(false);
178 callback_.Reset();
179 return true;
182 // DevToolsUIDefaultDelegate --------------------------------------------------
184 class DefaultBindingsDelegate : public DevToolsUIBindings::Delegate {
185 public:
186 explicit DefaultBindingsDelegate(content::WebContents* web_contents)
187 : web_contents_(web_contents) {}
189 private:
190 ~DefaultBindingsDelegate() override {}
192 void ActivateWindow() override;
193 void CloseWindow() override {}
194 void SetInspectedPageBounds(const gfx::Rect& rect) override {}
195 void InspectElementCompleted() override {}
196 void SetIsDocked(bool is_docked) override {}
197 void OpenInNewTab(const std::string& url) override;
198 void SetWhitelistedShortcuts(const std::string& message) override {}
199 using DispatchCallback =
200 DevToolsEmbedderMessageDispatcher::Delegate::DispatchCallback;
202 void InspectedContentsClosing() override;
203 void OnLoadCompleted() override {}
204 InfoBarService* GetInfoBarService() override;
205 void RenderProcessGone(bool crashed) override {}
207 content::WebContents* web_contents_;
208 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
211 void DefaultBindingsDelegate::ActivateWindow() {
212 web_contents_->GetDelegate()->ActivateContents(web_contents_);
213 web_contents_->Focus();
216 void DefaultBindingsDelegate::OpenInNewTab(const std::string& url) {
217 content::OpenURLParams params(
218 GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
219 ui::PAGE_TRANSITION_LINK, false);
220 Browser* browser = FindBrowser(web_contents_);
221 browser->OpenURL(params);
224 void DefaultBindingsDelegate::InspectedContentsClosing() {
225 web_contents_->ClosePage();
228 InfoBarService* DefaultBindingsDelegate::GetInfoBarService() {
229 return InfoBarService::FromWebContents(web_contents_);
232 // ResponseWriter -------------------------------------------------------------
234 class ResponseWriter : public net::URLFetcherResponseWriter {
235 public:
236 ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings, int stream_id);
237 ~ResponseWriter() override;
239 // URLFetcherResponseWriter overrides:
240 int Initialize(const net::CompletionCallback& callback) override;
241 int Write(net::IOBuffer* buffer,
242 int num_bytes,
243 const net::CompletionCallback& callback) override;
244 int Finish(const net::CompletionCallback& callback) override;
246 private:
247 base::WeakPtr<DevToolsUIBindings> bindings_;
248 int stream_id_;
250 DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
253 ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings,
254 int stream_id)
255 : bindings_(bindings),
256 stream_id_(stream_id) {
259 ResponseWriter::~ResponseWriter() {
262 int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
263 return net::OK;
266 int ResponseWriter::Write(net::IOBuffer* buffer,
267 int num_bytes,
268 const net::CompletionCallback& callback) {
269 base::FundamentalValue* id = new base::FundamentalValue(stream_id_);
270 base::StringValue* chunk =
271 new base::StringValue(std::string(buffer->data(), num_bytes));
273 content::BrowserThread::PostTask(
274 content::BrowserThread::UI, FROM_HERE,
275 base::Bind(&DevToolsUIBindings::CallClientFunction,
276 bindings_, "DevToolsAPI.streamWrite",
277 base::Owned(id), base::Owned(chunk), nullptr));
278 return num_bytes;
281 int ResponseWriter::Finish(const net::CompletionCallback& callback) {
282 return net::OK;
285 } // namespace
287 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
289 class DevToolsUIBindings::FrontendWebContentsObserver
290 : public content::WebContentsObserver {
291 public:
292 explicit FrontendWebContentsObserver(DevToolsUIBindings* ui_bindings);
293 ~FrontendWebContentsObserver() override;
295 private:
296 // contents::WebContentsObserver:
297 void RenderProcessGone(base::TerminationStatus status) override;
298 // TODO(creis): Replace with RenderFrameCreated when http://crbug.com/425397
299 // is fixed. See also http://crbug.com/424641.
300 void AboutToNavigateRenderFrame(
301 content::RenderFrameHost* old_host,
302 content::RenderFrameHost* new_host) override;
303 void DocumentOnLoadCompletedInMainFrame() override;
304 void DidNavigateMainFrame(
305 const content::LoadCommittedDetails& details,
306 const content::FrameNavigateParams& params) override;
308 DevToolsUIBindings* devtools_bindings_;
309 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
312 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
313 DevToolsUIBindings* devtools_ui_bindings)
314 : WebContentsObserver(devtools_ui_bindings->web_contents()),
315 devtools_bindings_(devtools_ui_bindings) {
318 DevToolsUIBindings::FrontendWebContentsObserver::
319 ~FrontendWebContentsObserver() {
322 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
323 base::TerminationStatus status) {
324 bool crashed = true;
325 switch (status) {
326 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
327 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
328 #if defined(OS_CHROMEOS)
329 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
330 #endif
331 case base::TERMINATION_STATUS_PROCESS_CRASHED:
332 case base::TERMINATION_STATUS_LAUNCH_FAILED:
333 if (devtools_bindings_->agent_host_.get())
334 devtools_bindings_->Detach();
335 break;
336 default:
337 crashed = false;
338 break;
340 devtools_bindings_->delegate_->RenderProcessGone(crashed);
343 void DevToolsUIBindings::FrontendWebContentsObserver::
344 AboutToNavigateRenderFrame(content::RenderFrameHost* old_host,
345 content::RenderFrameHost* new_host) {
346 if (new_host->GetParent())
347 return;
348 devtools_bindings_->frontend_host_.reset(
349 content::DevToolsFrontendHost::Create(new_host,
350 devtools_bindings_));
353 void DevToolsUIBindings::FrontendWebContentsObserver::
354 DocumentOnLoadCompletedInMainFrame() {
355 devtools_bindings_->DocumentOnLoadCompletedInMainFrame();
358 void DevToolsUIBindings::FrontendWebContentsObserver::
359 DidNavigateMainFrame(const content::LoadCommittedDetails& details,
360 const content::FrameNavigateParams& params) {
361 devtools_bindings_->DidNavigateMainFrame();
364 // WebSocketAPIChannel --------------------------------------------------------
366 class DevToolsUIBindings::WebSocketAPIChannel :
367 public content::DevToolsExternalAgentProxyDelegate {
368 public :
369 explicit WebSocketAPIChannel(base::WeakPtr<DevToolsUIBindings> bindings);
370 ~WebSocketAPIChannel() override;
371 void DispatchOnClientHost(const std::string& message);
372 void ConnectionClosed();
374 private:
375 // content::DevToolsExternalAgentProxyDelegate implementation.
376 void Attach(content::DevToolsExternalAgentProxy* proxy) override;
377 void Detach() override;
378 void SendMessageToBackend(const std::string& message) override;
380 base::WeakPtr<DevToolsUIBindings> bindings_;
381 content::DevToolsExternalAgentProxy* attached_proxy_;
382 DISALLOW_COPY_AND_ASSIGN(WebSocketAPIChannel);
385 DevToolsUIBindings::WebSocketAPIChannel::WebSocketAPIChannel(
386 base::WeakPtr<DevToolsUIBindings> bindings)
387 : bindings_(bindings),
388 attached_proxy_(nullptr) {
389 bindings->open_api_channel_ = this;
392 DevToolsUIBindings::WebSocketAPIChannel::~WebSocketAPIChannel() {
393 if (bindings_)
394 bindings_->open_api_channel_ = nullptr;
397 void DevToolsUIBindings::WebSocketAPIChannel::DispatchOnClientHost(
398 const std::string& message) {
399 if (attached_proxy_)
400 attached_proxy_->DispatchOnClientHost(message);
403 void DevToolsUIBindings::WebSocketAPIChannel::ConnectionClosed() {
404 if (bindings_) {
405 bindings_->CallClientFunction("DevToolsAPI.frontendAPIDetached",
406 nullptr, nullptr, nullptr);
408 if (attached_proxy_)
409 attached_proxy_->ConnectionClosed();
412 void DevToolsUIBindings::WebSocketAPIChannel::Attach(
413 content::DevToolsExternalAgentProxy* proxy) {
414 attached_proxy_ = proxy;
415 if (bindings_) {
416 bindings_->CallClientFunction("DevToolsAPI.frontendAPIAttached",
417 nullptr, nullptr, nullptr);
421 void DevToolsUIBindings::WebSocketAPIChannel::Detach() {
422 attached_proxy_ = nullptr;
423 if (bindings_) {
424 bindings_->open_api_channel_ = nullptr;
425 bindings_->CallClientFunction("DevToolsAPI.frontendAPIDetached",
426 nullptr, nullptr, nullptr);
430 void DevToolsUIBindings::WebSocketAPIChannel::SendMessageToBackend(
431 const std::string& message) {
432 if (!bindings_)
433 return;
434 base::StringValue message_value(message);
435 bindings_->CallClientFunction("DevToolsAPI.dispatchFrontendAPIMessage",
436 &message_value, nullptr, nullptr);
439 // DevToolsUIBindings ---------------------------------------------------------
441 DevToolsUIBindings* DevToolsUIBindings::ForWebContents(
442 content::WebContents* web_contents) {
443 if (g_instances == NULL)
444 return NULL;
445 DevToolsUIBindingsList* instances = g_instances.Pointer();
446 for (DevToolsUIBindingsList::iterator it(instances->begin());
447 it != instances->end(); ++it) {
448 if ((*it)->web_contents() == web_contents)
449 return *it;
451 return NULL;
454 DevToolsUIBindings::DevToolsUIBindings(content::WebContents* web_contents)
455 : profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
456 android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_)),
457 web_contents_(web_contents),
458 delegate_(new DefaultBindingsDelegate(web_contents_)),
459 devices_updates_enabled_(false),
460 frontend_loaded_(false),
461 open_api_channel_(nullptr),
462 weak_factory_(this) {
463 g_instances.Get().push_back(this);
464 frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
465 web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false;
467 file_helper_.reset(new DevToolsFileHelper(web_contents_, profile_));
468 file_system_indexer_ = new DevToolsFileSystemIndexer();
469 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
470 web_contents_);
472 // Register on-load actions.
473 embedder_message_dispatcher_.reset(
474 DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
476 frontend_host_.reset(content::DevToolsFrontendHost::Create(
477 web_contents_->GetMainFrame(), this));
480 DevToolsUIBindings::~DevToolsUIBindings() {
481 for (const auto& pair : pending_requests_)
482 delete pair.first;
484 if (agent_host_.get())
485 agent_host_->DetachClient();
487 for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
488 jobs_it != indexing_jobs_.end(); ++jobs_it) {
489 jobs_it->second->Stop();
491 indexing_jobs_.clear();
492 SetDevicesUpdatesEnabled(false);
494 if (open_api_channel_)
495 open_api_channel_->ConnectionClosed();
497 // Remove self from global list.
498 DevToolsUIBindingsList* instances = g_instances.Pointer();
499 DevToolsUIBindingsList::iterator it(
500 std::find(instances->begin(), instances->end(), this));
501 DCHECK(it != instances->end());
502 instances->erase(it);
505 // content::DevToolsFrontendHost::Delegate implementation ---------------------
506 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
507 const std::string& message) {
508 std::string method;
509 base::ListValue empty_params;
510 base::ListValue* params = &empty_params;
512 base::DictionaryValue* dict = NULL;
513 scoped_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
514 if (!parsed_message ||
515 !parsed_message->GetAsDictionary(&dict) ||
516 !dict->GetString(kFrontendHostMethod, &method) ||
517 (dict->HasKey(kFrontendHostParams) &&
518 !dict->GetList(kFrontendHostParams, &params))) {
519 LOG(ERROR) << "Invalid message was sent to embedder: " << message;
520 return;
522 int id = 0;
523 dict->GetInteger(kFrontendHostId, &id);
524 embedder_message_dispatcher_->Dispatch(
525 base::Bind(&DevToolsUIBindings::SendMessageAck,
526 weak_factory_.GetWeakPtr(),
527 id),
528 method,
529 params);
532 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
533 const std::string& message) {
534 if (agent_host_.get())
535 agent_host_->DispatchProtocolMessage(message);
538 // content::DevToolsAgentHostClient implementation --------------------------
539 void DevToolsUIBindings::DispatchProtocolMessage(
540 content::DevToolsAgentHost* agent_host, const std::string& message) {
541 DCHECK(agent_host == agent_host_.get());
543 if (message.length() < kMaxMessageChunkSize) {
544 base::string16 javascript = base::UTF8ToUTF16(
545 "DevToolsAPI.dispatchMessage(" + message + ");");
546 web_contents_->GetMainFrame()->ExecuteJavaScript(javascript);
547 return;
550 base::FundamentalValue total_size(static_cast<int>(message.length()));
551 for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
552 base::StringValue message_value(message.substr(pos, kMaxMessageChunkSize));
553 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
554 &message_value, pos ? NULL : &total_size, NULL);
558 void DevToolsUIBindings::AgentHostClosed(
559 content::DevToolsAgentHost* agent_host,
560 bool replaced_with_another_client) {
561 DCHECK(agent_host == agent_host_.get());
562 agent_host_ = NULL;
563 delegate_->InspectedContentsClosing();
566 void DevToolsUIBindings::SendMessageAck(int request_id,
567 const base::Value* arg) {
568 base::FundamentalValue id_value(request_id);
569 CallClientFunction("DevToolsAPI.embedderMessageAck",
570 &id_value, arg, nullptr);
573 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
574 void DevToolsUIBindings::ActivateWindow() {
575 delegate_->ActivateWindow();
578 void DevToolsUIBindings::CloseWindow() {
579 delegate_->CloseWindow();
582 void DevToolsUIBindings::LoadCompleted() {
583 FrontendLoaded();
586 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) {
587 delegate_->SetInspectedPageBounds(rect);
590 void DevToolsUIBindings::SetIsDocked(const DispatchCallback& callback,
591 bool dock_requested) {
592 delegate_->SetIsDocked(dock_requested);
593 callback.Run(nullptr);
596 void DevToolsUIBindings::InspectElementCompleted() {
597 delegate_->InspectElementCompleted();
600 void DevToolsUIBindings::InspectedURLChanged(const std::string& url) {
601 content::NavigationController& controller = web_contents()->GetController();
602 content::NavigationEntry* entry = controller.GetActiveEntry();
603 // DevTools UI is not localized.
604 entry->SetTitle(
605 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
606 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
609 void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback,
610 const std::string& url,
611 const std::string& headers,
612 int stream_id) {
613 GURL gurl(url);
614 if (!gurl.is_valid()) {
615 base::DictionaryValue response;
616 response.SetInteger("statusCode", 404);
617 callback.Run(&response);
618 return;
621 net::URLFetcher* fetcher =
622 net::URLFetcher::Create(gurl, net::URLFetcher::GET, this).release();
623 pending_requests_[fetcher] = callback;
624 fetcher->SetRequestContext(profile_->GetRequestContext());
625 fetcher->SetExtraRequestHeaders(headers);
626 fetcher->SaveResponseWithWriter(scoped_ptr<net::URLFetcherResponseWriter>(
627 new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
628 fetcher->Start();
631 void DevToolsUIBindings::OpenInNewTab(const std::string& url) {
632 delegate_->OpenInNewTab(url);
635 void DevToolsUIBindings::SaveToFile(const std::string& url,
636 const std::string& content,
637 bool save_as) {
638 file_helper_->Save(url, content, save_as,
639 base::Bind(&DevToolsUIBindings::FileSavedAs,
640 weak_factory_.GetWeakPtr(), url),
641 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs,
642 weak_factory_.GetWeakPtr(), url));
645 void DevToolsUIBindings::AppendToFile(const std::string& url,
646 const std::string& content) {
647 file_helper_->Append(url, content,
648 base::Bind(&DevToolsUIBindings::AppendedTo,
649 weak_factory_.GetWeakPtr(), url));
652 void DevToolsUIBindings::RequestFileSystems() {
653 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
654 file_helper_->RequestFileSystems(base::Bind(
655 &DevToolsUIBindings::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
658 void DevToolsUIBindings::AddFileSystem() {
659 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
660 file_helper_->AddFileSystem(
661 base::Bind(&DevToolsUIBindings::FileSystemAdded,
662 weak_factory_.GetWeakPtr()),
663 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
664 weak_factory_.GetWeakPtr()));
667 void DevToolsUIBindings::RemoveFileSystem(const std::string& file_system_path) {
668 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
669 file_helper_->RemoveFileSystem(file_system_path);
670 base::StringValue file_system_path_value(file_system_path);
671 CallClientFunction("DevToolsAPI.fileSystemRemoved",
672 &file_system_path_value, NULL, NULL);
675 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
676 const std::string& file_system_url) {
677 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
678 file_helper_->UpgradeDraggedFileSystemPermissions(
679 file_system_url,
680 base::Bind(&DevToolsUIBindings::FileSystemAdded,
681 weak_factory_.GetWeakPtr()),
682 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
683 weak_factory_.GetWeakPtr()));
686 void DevToolsUIBindings::IndexPath(int index_request_id,
687 const std::string& file_system_path) {
688 DCHECK_CURRENTLY_ON(BrowserThread::UI);
689 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
690 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
691 IndexingDone(index_request_id, file_system_path);
692 return;
694 if (indexing_jobs_.count(index_request_id) != 0)
695 return;
696 indexing_jobs_[index_request_id] =
697 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
698 file_system_indexer_->IndexPath(
699 file_system_path,
700 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated,
701 weak_factory_.GetWeakPtr(),
702 index_request_id,
703 file_system_path),
704 Bind(&DevToolsUIBindings::IndexingWorked,
705 weak_factory_.GetWeakPtr(),
706 index_request_id,
707 file_system_path),
708 Bind(&DevToolsUIBindings::IndexingDone,
709 weak_factory_.GetWeakPtr(),
710 index_request_id,
711 file_system_path)));
714 void DevToolsUIBindings::StopIndexing(int index_request_id) {
715 DCHECK_CURRENTLY_ON(BrowserThread::UI);
716 IndexingJobsMap::iterator it = indexing_jobs_.find(index_request_id);
717 if (it == indexing_jobs_.end())
718 return;
719 it->second->Stop();
720 indexing_jobs_.erase(it);
723 void DevToolsUIBindings::SearchInPath(int search_request_id,
724 const std::string& file_system_path,
725 const std::string& query) {
726 DCHECK_CURRENTLY_ON(BrowserThread::UI);
727 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
728 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
729 SearchCompleted(search_request_id,
730 file_system_path,
731 std::vector<std::string>());
732 return;
734 file_system_indexer_->SearchInPath(file_system_path,
735 query,
736 Bind(&DevToolsUIBindings::SearchCompleted,
737 weak_factory_.GetWeakPtr(),
738 search_request_id,
739 file_system_path));
742 void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string& message) {
743 delegate_->SetWhitelistedShortcuts(message);
746 void DevToolsUIBindings::ZoomIn() {
747 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN);
750 void DevToolsUIBindings::ZoomOut() {
751 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT);
754 void DevToolsUIBindings::ResetZoom() {
755 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
758 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled) {
759 if (devices_updates_enabled_ == enabled)
760 return;
761 devices_updates_enabled_ = enabled;
762 if (enabled) {
763 remote_targets_handler_ = DevToolsTargetsUIHandler::CreateForAdb(
764 base::Bind(&DevToolsUIBindings::DevicesUpdated,
765 base::Unretained(this)),
766 profile_);
767 } else {
768 remote_targets_handler_.reset();
772 void DevToolsUIBindings::GetPreferences(const DispatchCallback& callback) {
773 const DictionaryValue* prefs =
774 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsPreferences);
775 callback.Run(prefs);
778 void DevToolsUIBindings::SetPreference(const std::string& name,
779 const std::string& value) {
780 DictionaryPrefUpdate update(profile_->GetPrefs(),
781 prefs::kDevToolsPreferences);
782 update.Get()->SetStringWithoutPathExpansion(name, value);
785 void DevToolsUIBindings::RemovePreference(const std::string& name) {
786 DictionaryPrefUpdate update(profile_->GetPrefs(),
787 prefs::kDevToolsPreferences);
788 update.Get()->RemoveWithoutPathExpansion(name, nullptr);
791 void DevToolsUIBindings::ClearPreferences() {
792 DictionaryPrefUpdate update(profile_->GetPrefs(),
793 prefs::kDevToolsPreferences);
794 update.Get()->Clear();
797 void DevToolsUIBindings::SendMessageToBrowser(const std::string& message) {
798 if (agent_host_.get())
799 agent_host_->DispatchProtocolMessage(message);
802 void DevToolsUIBindings::RecordEnumeratedHistogram(const std::string& name,
803 int sample,
804 int boundary_value) {
805 if (!(boundary_value >= 0 && boundary_value <= 100 && sample >= 0 &&
806 sample < boundary_value)) {
807 // TODO(nick): Replace with chrome::bad_message::ReceivedBadMessage().
808 frontend_host_->BadMessageRecieved();
809 return;
811 // Each histogram name must follow a different code path in
812 // order to UMA_HISTOGRAM_ENUMERATION work correctly.
813 if (name == kDevToolsActionTakenHistogram)
814 UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value);
815 else if (name == kDevToolsPanelShownHistogram)
816 UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value);
817 else
818 frontend_host_->BadMessageRecieved();
821 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback& callback,
822 const std::string& browser_id,
823 const std::string& url) {
824 if (!android_bridge_) {
825 callback.Run(nullptr);
826 return;
828 android_bridge_->SendJsonRequest(browser_id, url,
829 base::Bind(&DevToolsUIBindings::JsonReceived,
830 weak_factory_.GetWeakPtr(),
831 callback));
834 void DevToolsUIBindings::SendFrontendAPINotification(
835 const std::string& message) {
836 if (!open_api_channel_)
837 return;
838 open_api_channel_->DispatchOnClientHost(message);
841 void DevToolsUIBindings::JsonReceived(const DispatchCallback& callback,
842 int result,
843 const std::string& message) {
844 if (result != net::OK) {
845 callback.Run(nullptr);
846 return;
848 base::StringValue message_value(message);
849 callback.Run(&message_value);
852 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher* source) {
853 DCHECK(source);
854 PendingRequestsMap::iterator it = pending_requests_.find(source);
855 DCHECK(it != pending_requests_.end());
857 base::DictionaryValue response;
858 base::DictionaryValue* headers = new base::DictionaryValue();
859 net::HttpResponseHeaders* rh = source->GetResponseHeaders();
860 response.SetInteger("statusCode", rh ? rh->response_code() : 200);
861 response.Set("headers", headers);
863 void* iterator = NULL;
864 std::string name;
865 std::string value;
866 while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
867 headers->SetString(name, value);
869 it->second.Run(&response);
870 pending_requests_.erase(it);
871 delete source;
874 void DevToolsUIBindings::DeviceCountChanged(int count) {
875 base::FundamentalValue value(count);
876 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value, NULL,
877 NULL);
880 void DevToolsUIBindings::DevicesUpdated(
881 const std::string& source,
882 const base::ListValue& targets) {
883 CallClientFunction("DevToolsAPI.devicesUpdated", &targets, NULL,
884 NULL);
887 void DevToolsUIBindings::FileSavedAs(const std::string& url) {
888 base::StringValue url_value(url);
889 CallClientFunction("DevToolsAPI.savedURL", &url_value, NULL, NULL);
892 void DevToolsUIBindings::CanceledFileSaveAs(const std::string& url) {
893 base::StringValue url_value(url);
894 CallClientFunction("DevToolsAPI.canceledSaveURL",
895 &url_value, NULL, NULL);
898 void DevToolsUIBindings::AppendedTo(const std::string& url) {
899 base::StringValue url_value(url);
900 CallClientFunction("DevToolsAPI.appendedToURL", &url_value, NULL,
901 NULL);
904 void DevToolsUIBindings::FileSystemsLoaded(
905 const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
906 base::ListValue file_systems_value;
907 for (size_t i = 0; i < file_systems.size(); ++i)
908 file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
909 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
910 &file_systems_value, NULL, NULL);
913 void DevToolsUIBindings::FileSystemAdded(
914 const DevToolsFileHelper::FileSystem& file_system) {
915 scoped_ptr<base::StringValue> error_string_value(
916 new base::StringValue(std::string()));
917 scoped_ptr<base::DictionaryValue> file_system_value;
918 if (!file_system.file_system_path.empty())
919 file_system_value.reset(CreateFileSystemValue(file_system));
920 CallClientFunction("DevToolsAPI.fileSystemAdded",
921 error_string_value.get(), file_system_value.get(), NULL);
924 void DevToolsUIBindings::IndexingTotalWorkCalculated(
925 int request_id,
926 const std::string& file_system_path,
927 int total_work) {
928 DCHECK_CURRENTLY_ON(BrowserThread::UI);
929 base::FundamentalValue request_id_value(request_id);
930 base::StringValue file_system_path_value(file_system_path);
931 base::FundamentalValue total_work_value(total_work);
932 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
933 &request_id_value, &file_system_path_value,
934 &total_work_value);
937 void DevToolsUIBindings::IndexingWorked(int request_id,
938 const std::string& file_system_path,
939 int worked) {
940 DCHECK_CURRENTLY_ON(BrowserThread::UI);
941 base::FundamentalValue request_id_value(request_id);
942 base::StringValue file_system_path_value(file_system_path);
943 base::FundamentalValue worked_value(worked);
944 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value,
945 &file_system_path_value, &worked_value);
948 void DevToolsUIBindings::IndexingDone(int request_id,
949 const std::string& file_system_path) {
950 indexing_jobs_.erase(request_id);
951 DCHECK_CURRENTLY_ON(BrowserThread::UI);
952 base::FundamentalValue request_id_value(request_id);
953 base::StringValue file_system_path_value(file_system_path);
954 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value,
955 &file_system_path_value, NULL);
958 void DevToolsUIBindings::SearchCompleted(
959 int request_id,
960 const std::string& file_system_path,
961 const std::vector<std::string>& file_paths) {
962 DCHECK_CURRENTLY_ON(BrowserThread::UI);
963 base::ListValue file_paths_value;
964 for (std::vector<std::string>::const_iterator it(file_paths.begin());
965 it != file_paths.end(); ++it) {
966 file_paths_value.AppendString(*it);
968 base::FundamentalValue request_id_value(request_id);
969 base::StringValue file_system_path_value(file_system_path);
970 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value,
971 &file_system_path_value, &file_paths_value);
974 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
975 const base::string16& message,
976 const InfoBarCallback& callback) {
977 DevToolsConfirmInfoBarDelegate::Create(delegate_->GetInfoBarService(),
978 callback, message);
981 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
982 const extensions::ExtensionRegistry* registry =
983 extensions::ExtensionRegistry::Get(profile_->GetOriginalProfile());
984 if (!registry)
985 return;
987 base::ListValue results;
988 for (const scoped_refptr<const extensions::Extension>& extension :
989 registry->enabled_extensions()) {
990 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
991 .is_empty())
992 continue;
993 base::DictionaryValue* extension_info = new base::DictionaryValue();
994 extension_info->Set(
995 "startPage",
996 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
997 extension.get()).spec()));
998 extension_info->Set("name", new base::StringValue(extension->name()));
999 extension_info->Set("exposeExperimentalAPIs",
1000 new base::FundamentalValue(
1001 extension->permissions_data()->HasAPIPermission(
1002 extensions::APIPermission::kExperimental)));
1003 results.Append(extension_info);
1005 CallClientFunction("DevToolsAPI.addExtensions",
1006 &results, NULL, NULL);
1009 void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
1010 delegate_.reset(delegate);
1013 void DevToolsUIBindings::AttachTo(
1014 const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
1015 if (agent_host_.get())
1016 Detach();
1017 agent_host_ = agent_host;
1018 agent_host_->AttachClient(this);
1021 void DevToolsUIBindings::Reattach() {
1022 DCHECK(agent_host_.get());
1023 agent_host_->DetachClient();
1024 agent_host_->AttachClient(this);
1027 void DevToolsUIBindings::Detach() {
1028 if (agent_host_.get())
1029 agent_host_->DetachClient();
1030 agent_host_ = NULL;
1033 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost* agent_host) {
1034 return agent_host_.get() == agent_host;
1037 content::DevToolsExternalAgentProxyDelegate*
1038 DevToolsUIBindings::CreateWebSocketAPIChannel() {
1039 if (open_api_channel_)
1040 open_api_channel_->ConnectionClosed();
1041 return new WebSocketAPIChannel(weak_factory_.GetWeakPtr());
1044 void DevToolsUIBindings::CallClientFunction(const std::string& function_name,
1045 const base::Value* arg1,
1046 const base::Value* arg2,
1047 const base::Value* arg3) {
1048 std::string javascript = function_name + "(";
1049 if (arg1) {
1050 std::string json;
1051 base::JSONWriter::Write(*arg1, &json);
1052 javascript.append(json);
1053 if (arg2) {
1054 base::JSONWriter::Write(*arg2, &json);
1055 javascript.append(", ").append(json);
1056 if (arg3) {
1057 base::JSONWriter::Write(*arg3, &json);
1058 javascript.append(", ").append(json);
1062 javascript.append(");");
1063 web_contents_->GetMainFrame()->ExecuteJavaScript(
1064 base::UTF8ToUTF16(javascript));
1067 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
1068 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
1069 // arrives before the LoadCompleted event, thus it should not trigger the
1070 // frontend load handling.
1071 #if !defined(DEBUG_DEVTOOLS)
1072 FrontendLoaded();
1073 #endif
1076 void DevToolsUIBindings::DidNavigateMainFrame() {
1077 frontend_loaded_ = false;
1080 void DevToolsUIBindings::FrontendLoaded() {
1081 if (frontend_loaded_)
1082 return;
1083 frontend_loaded_ = true;
1085 // Call delegate first - it seeds importants bit of information.
1086 delegate_->OnLoadCompleted();
1088 AddDevToolsExtensionsToClient();