No dual_mode on Win10+ shortcuts.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_ui_bindings.cc
blob7774ddc30efae194c16d59602978c29eedf1181b
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/favicon_status.h"
36 #include "content/public/browser/invalidate_type.h"
37 #include "content/public/browser/navigation_controller.h"
38 #include "content/public/browser/navigation_entry.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/render_view_host.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_observer.h"
45 #include "content/public/common/renderer_preferences.h"
46 #include "content/public/common/url_constants.h"
47 #include "extensions/browser/extension_registry.h"
48 #include "extensions/common/permissions/permissions_data.h"
49 #include "net/base/io_buffer.h"
50 #include "net/base/net_errors.h"
51 #include "net/http/http_response_headers.h"
52 #include "net/url_request/url_fetcher.h"
53 #include "net/url_request/url_fetcher_response_writer.h"
54 #include "ui/base/l10n/l10n_util.h"
55 #include "ui/base/page_transition_types.h"
57 using base::DictionaryValue;
58 using content::BrowserThread;
60 namespace content {
61 struct LoadCommittedDetails;
62 struct FrameNavigateParams;
65 namespace {
67 static const char kFrontendHostId[] = "id";
68 static const char kFrontendHostMethod[] = "method";
69 static const char kFrontendHostParams[] = "params";
70 static const char kTitleFormat[] = "Developer Tools - %s";
72 static const char kDevToolsActionTakenHistogram[] = "DevTools.ActionTaken";
73 static const char kDevToolsPanelShownHistogram[] = "DevTools.PanelShown";
75 // This constant should be in sync with
76 // the constant at shell_devtools_frontend.cc.
77 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
79 typedef std::vector<DevToolsUIBindings*> DevToolsUIBindingsList;
80 base::LazyInstance<DevToolsUIBindingsList>::Leaky g_instances =
81 LAZY_INSTANCE_INITIALIZER;
83 base::DictionaryValue* CreateFileSystemValue(
84 DevToolsFileHelper::FileSystem file_system) {
85 base::DictionaryValue* file_system_value = new base::DictionaryValue();
86 file_system_value->SetString("fileSystemName", file_system.file_system_name);
87 file_system_value->SetString("rootURL", file_system.root_url);
88 file_system_value->SetString("fileSystemPath", file_system.file_system_path);
89 return file_system_value;
92 Browser* FindBrowser(content::WebContents* web_contents) {
93 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
94 int tab_index = it->tab_strip_model()->GetIndexOfWebContents(
95 web_contents);
96 if (tab_index != TabStripModel::kNoTab)
97 return *it;
99 return NULL;
102 // DevToolsConfirmInfoBarDelegate ---------------------------------------------
104 typedef base::Callback<void(bool)> InfoBarCallback;
106 class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
107 public:
108 // If |infobar_service| is NULL, runs |callback| with a single argument with
109 // value "false". Otherwise, creates a dev tools confirm infobar and delegate
110 // and adds the infobar to |infobar_service|.
111 static void Create(InfoBarService* infobar_service,
112 const InfoBarCallback& callback,
113 const base::string16& message);
115 private:
116 DevToolsConfirmInfoBarDelegate(
117 const InfoBarCallback& callback,
118 const base::string16& message);
119 ~DevToolsConfirmInfoBarDelegate() override;
121 base::string16 GetMessageText() const override;
122 base::string16 GetButtonLabel(InfoBarButton button) const override;
123 bool Accept() override;
124 bool Cancel() override;
126 InfoBarCallback callback_;
127 const base::string16 message_;
129 DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
132 void DevToolsConfirmInfoBarDelegate::Create(
133 InfoBarService* infobar_service,
134 const InfoBarCallback& callback,
135 const base::string16& message) {
136 if (!infobar_service) {
137 callback.Run(false);
138 return;
141 infobar_service->AddInfoBar(
142 infobar_service->CreateConfirmInfoBar(scoped_ptr<ConfirmInfoBarDelegate>(
143 new DevToolsConfirmInfoBarDelegate(callback, message))));
146 DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
147 const InfoBarCallback& callback,
148 const base::string16& message)
149 : ConfirmInfoBarDelegate(),
150 callback_(callback),
151 message_(message) {
154 DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
155 if (!callback_.is_null())
156 callback_.Run(false);
159 base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
160 return message_;
163 base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
164 InfoBarButton button) const {
165 return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
166 IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
169 bool DevToolsConfirmInfoBarDelegate::Accept() {
170 callback_.Run(true);
171 callback_.Reset();
172 return true;
175 bool DevToolsConfirmInfoBarDelegate::Cancel() {
176 callback_.Run(false);
177 callback_.Reset();
178 return true;
181 // DevToolsUIDefaultDelegate --------------------------------------------------
183 class DefaultBindingsDelegate : public DevToolsUIBindings::Delegate {
184 public:
185 explicit DefaultBindingsDelegate(content::WebContents* web_contents)
186 : web_contents_(web_contents) {}
188 private:
189 ~DefaultBindingsDelegate() override {}
191 void ActivateWindow() override;
192 void CloseWindow() override {}
193 void SetInspectedPageBounds(const gfx::Rect& rect) override {}
194 void InspectElementCompleted() override {}
195 void SetIsDocked(bool is_docked) override {}
196 void OpenInNewTab(const std::string& url) override;
197 void SetWhitelistedShortcuts(const std::string& message) override {}
198 using DispatchCallback =
199 DevToolsEmbedderMessageDispatcher::Delegate::DispatchCallback;
201 void InspectedContentsClosing() override;
202 void OnLoadCompleted() override {}
203 InfoBarService* GetInfoBarService() override;
204 void RenderProcessGone(bool crashed) override {}
206 content::WebContents* web_contents_;
207 DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
210 void DefaultBindingsDelegate::ActivateWindow() {
211 web_contents_->GetDelegate()->ActivateContents(web_contents_);
212 web_contents_->Focus();
215 void DefaultBindingsDelegate::OpenInNewTab(const std::string& url) {
216 content::OpenURLParams params(
217 GURL(url), content::Referrer(), NEW_FOREGROUND_TAB,
218 ui::PAGE_TRANSITION_LINK, false);
219 Browser* browser = FindBrowser(web_contents_);
220 browser->OpenURL(params);
223 void DefaultBindingsDelegate::InspectedContentsClosing() {
224 web_contents_->ClosePage();
227 InfoBarService* DefaultBindingsDelegate::GetInfoBarService() {
228 return InfoBarService::FromWebContents(web_contents_);
231 // ResponseWriter -------------------------------------------------------------
233 class ResponseWriter : public net::URLFetcherResponseWriter {
234 public:
235 ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings, int stream_id);
236 ~ResponseWriter() override;
238 // URLFetcherResponseWriter overrides:
239 int Initialize(const net::CompletionCallback& callback) override;
240 int Write(net::IOBuffer* buffer,
241 int num_bytes,
242 const net::CompletionCallback& callback) override;
243 int Finish(const net::CompletionCallback& callback) override;
245 private:
246 base::WeakPtr<DevToolsUIBindings> bindings_;
247 int stream_id_;
249 DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
252 ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings,
253 int stream_id)
254 : bindings_(bindings),
255 stream_id_(stream_id) {
258 ResponseWriter::~ResponseWriter() {
261 int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
262 return net::OK;
265 int ResponseWriter::Write(net::IOBuffer* buffer,
266 int num_bytes,
267 const net::CompletionCallback& callback) {
268 base::FundamentalValue* id = new base::FundamentalValue(stream_id_);
269 base::StringValue* chunk =
270 new base::StringValue(std::string(buffer->data(), num_bytes));
272 content::BrowserThread::PostTask(
273 content::BrowserThread::UI, FROM_HERE,
274 base::Bind(&DevToolsUIBindings::CallClientFunction,
275 bindings_, "DevToolsAPI.streamWrite",
276 base::Owned(id), base::Owned(chunk), nullptr));
277 return num_bytes;
280 int ResponseWriter::Finish(const net::CompletionCallback& callback) {
281 return net::OK;
284 } // namespace
286 // DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
288 class DevToolsUIBindings::FrontendWebContentsObserver
289 : public content::WebContentsObserver {
290 public:
291 explicit FrontendWebContentsObserver(DevToolsUIBindings* ui_bindings);
292 ~FrontendWebContentsObserver() override;
294 private:
295 // contents::WebContentsObserver:
296 void RenderProcessGone(base::TerminationStatus status) override;
297 // TODO(creis): Replace with RenderFrameCreated when http://crbug.com/425397
298 // is fixed. See also http://crbug.com/424641.
299 void AboutToNavigateRenderFrame(
300 content::RenderFrameHost* old_host,
301 content::RenderFrameHost* new_host) override;
302 void DocumentOnLoadCompletedInMainFrame() override;
303 void DidNavigateMainFrame(
304 const content::LoadCommittedDetails& details,
305 const content::FrameNavigateParams& params) override;
307 DevToolsUIBindings* devtools_bindings_;
308 DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
311 DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
312 DevToolsUIBindings* devtools_ui_bindings)
313 : WebContentsObserver(devtools_ui_bindings->web_contents()),
314 devtools_bindings_(devtools_ui_bindings) {
317 DevToolsUIBindings::FrontendWebContentsObserver::
318 ~FrontendWebContentsObserver() {
321 void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
322 base::TerminationStatus status) {
323 bool crashed = true;
324 switch (status) {
325 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
326 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
327 #if defined(OS_CHROMEOS)
328 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
329 #endif
330 case base::TERMINATION_STATUS_PROCESS_CRASHED:
331 if (devtools_bindings_->agent_host_.get())
332 devtools_bindings_->Detach();
333 break;
334 default:
335 crashed = false;
336 break;
338 devtools_bindings_->delegate_->RenderProcessGone(crashed);
341 void DevToolsUIBindings::FrontendWebContentsObserver::
342 AboutToNavigateRenderFrame(content::RenderFrameHost* old_host,
343 content::RenderFrameHost* new_host) {
344 if (new_host->GetParent())
345 return;
346 devtools_bindings_->frontend_host_.reset(
347 content::DevToolsFrontendHost::Create(new_host,
348 devtools_bindings_));
351 void DevToolsUIBindings::FrontendWebContentsObserver::
352 DocumentOnLoadCompletedInMainFrame() {
353 devtools_bindings_->DocumentOnLoadCompletedInMainFrame();
356 void DevToolsUIBindings::FrontendWebContentsObserver::
357 DidNavigateMainFrame(const content::LoadCommittedDetails& details,
358 const content::FrameNavigateParams& params) {
359 devtools_bindings_->DidNavigateMainFrame();
362 // DevToolsUIBindings ---------------------------------------------------------
364 DevToolsUIBindings* DevToolsUIBindings::ForWebContents(
365 content::WebContents* web_contents) {
366 if (g_instances == NULL)
367 return NULL;
368 DevToolsUIBindingsList* instances = g_instances.Pointer();
369 for (DevToolsUIBindingsList::iterator it(instances->begin());
370 it != instances->end(); ++it) {
371 if ((*it)->web_contents() == web_contents)
372 return *it;
374 return NULL;
377 DevToolsUIBindings::DevToolsUIBindings(content::WebContents* web_contents)
378 : profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
379 android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_)),
380 web_contents_(web_contents),
381 delegate_(new DefaultBindingsDelegate(web_contents_)),
382 devices_updates_enabled_(false),
383 frontend_loaded_(false),
384 weak_factory_(this) {
385 g_instances.Get().push_back(this);
386 frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
387 web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false;
389 file_helper_.reset(new DevToolsFileHelper(web_contents_, profile_));
390 file_system_indexer_ = new DevToolsFileSystemIndexer();
391 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
392 web_contents_);
394 // Wipe out page icon so that the default application icon is used.
395 content::NavigationEntry* entry =
396 web_contents_->GetController().GetActiveEntry();
397 entry->GetFavicon().image = gfx::Image();
398 entry->GetFavicon().valid = true;
400 // Register on-load actions.
401 embedder_message_dispatcher_.reset(
402 DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
404 frontend_host_.reset(content::DevToolsFrontendHost::Create(
405 web_contents_->GetMainFrame(), this));
408 DevToolsUIBindings::~DevToolsUIBindings() {
409 for (const auto& pair : pending_requests_)
410 delete pair.first;
412 if (agent_host_.get())
413 agent_host_->DetachClient();
415 for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
416 jobs_it != indexing_jobs_.end(); ++jobs_it) {
417 jobs_it->second->Stop();
419 indexing_jobs_.clear();
420 SetDevicesUpdatesEnabled(false);
422 // Remove self from global list.
423 DevToolsUIBindingsList* instances = g_instances.Pointer();
424 DevToolsUIBindingsList::iterator it(
425 std::find(instances->begin(), instances->end(), this));
426 DCHECK(it != instances->end());
427 instances->erase(it);
430 // content::DevToolsFrontendHost::Delegate implementation ---------------------
431 void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
432 const std::string& message) {
433 std::string method;
434 base::ListValue empty_params;
435 base::ListValue* params = &empty_params;
437 base::DictionaryValue* dict = NULL;
438 scoped_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
439 if (!parsed_message ||
440 !parsed_message->GetAsDictionary(&dict) ||
441 !dict->GetString(kFrontendHostMethod, &method) ||
442 (dict->HasKey(kFrontendHostParams) &&
443 !dict->GetList(kFrontendHostParams, &params))) {
444 LOG(ERROR) << "Invalid message was sent to embedder: " << message;
445 return;
447 int id = 0;
448 dict->GetInteger(kFrontendHostId, &id);
449 embedder_message_dispatcher_->Dispatch(
450 base::Bind(&DevToolsUIBindings::SendMessageAck,
451 weak_factory_.GetWeakPtr(),
452 id),
453 method,
454 params);
457 void DevToolsUIBindings::HandleMessageFromDevToolsFrontendToBackend(
458 const std::string& message) {
459 if (agent_host_.get())
460 agent_host_->DispatchProtocolMessage(message);
463 // content::DevToolsAgentHostClient implementation --------------------------
464 void DevToolsUIBindings::DispatchProtocolMessage(
465 content::DevToolsAgentHost* agent_host, const std::string& message) {
466 DCHECK(agent_host == agent_host_.get());
468 if (message.length() < kMaxMessageChunkSize) {
469 base::string16 javascript = base::UTF8ToUTF16(
470 "DevToolsAPI.dispatchMessage(" + message + ");");
471 web_contents_->GetMainFrame()->ExecuteJavaScript(javascript);
472 return;
475 base::FundamentalValue total_size(static_cast<int>(message.length()));
476 for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
477 base::StringValue message_value(message.substr(pos, kMaxMessageChunkSize));
478 CallClientFunction("DevToolsAPI.dispatchMessageChunk",
479 &message_value, pos ? NULL : &total_size, NULL);
483 void DevToolsUIBindings::AgentHostClosed(
484 content::DevToolsAgentHost* agent_host,
485 bool replaced_with_another_client) {
486 DCHECK(agent_host == agent_host_.get());
487 agent_host_ = NULL;
488 delegate_->InspectedContentsClosing();
491 void DevToolsUIBindings::SendMessageAck(int request_id,
492 const base::Value* arg) {
493 base::FundamentalValue id_value(request_id);
494 CallClientFunction("DevToolsAPI.embedderMessageAck",
495 &id_value, arg, nullptr);
498 // DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
499 void DevToolsUIBindings::ActivateWindow() {
500 delegate_->ActivateWindow();
503 void DevToolsUIBindings::CloseWindow() {
504 delegate_->CloseWindow();
507 void DevToolsUIBindings::LoadCompleted() {
508 FrontendLoaded();
511 void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) {
512 delegate_->SetInspectedPageBounds(rect);
515 void DevToolsUIBindings::SetIsDocked(const DispatchCallback& callback,
516 bool dock_requested) {
517 delegate_->SetIsDocked(dock_requested);
518 callback.Run(nullptr);
521 void DevToolsUIBindings::InspectElementCompleted() {
522 delegate_->InspectElementCompleted();
525 void DevToolsUIBindings::InspectedURLChanged(const std::string& url) {
526 content::NavigationController& controller = web_contents()->GetController();
527 content::NavigationEntry* entry = controller.GetActiveEntry();
528 // DevTools UI is not localized.
529 entry->SetTitle(
530 base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
531 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
534 void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback,
535 const std::string& url,
536 const std::string& headers,
537 int stream_id) {
538 GURL gurl(url);
539 if (!gurl.is_valid()) {
540 base::DictionaryValue response;
541 response.SetInteger("statusCode", 404);
542 callback.Run(&response);
543 return;
546 net::URLFetcher* fetcher =
547 net::URLFetcher::Create(gurl, net::URLFetcher::GET, this).release();
548 pending_requests_[fetcher] = callback;
549 fetcher->SetRequestContext(profile_->GetRequestContext());
550 fetcher->SetExtraRequestHeaders(headers);
551 fetcher->SaveResponseWithWriter(scoped_ptr<net::URLFetcherResponseWriter>(
552 new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
553 fetcher->Start();
556 void DevToolsUIBindings::OpenInNewTab(const std::string& url) {
557 delegate_->OpenInNewTab(url);
560 void DevToolsUIBindings::SaveToFile(const std::string& url,
561 const std::string& content,
562 bool save_as) {
563 file_helper_->Save(url, content, save_as,
564 base::Bind(&DevToolsUIBindings::FileSavedAs,
565 weak_factory_.GetWeakPtr(), url),
566 base::Bind(&DevToolsUIBindings::CanceledFileSaveAs,
567 weak_factory_.GetWeakPtr(), url));
570 void DevToolsUIBindings::AppendToFile(const std::string& url,
571 const std::string& content) {
572 file_helper_->Append(url, content,
573 base::Bind(&DevToolsUIBindings::AppendedTo,
574 weak_factory_.GetWeakPtr(), url));
577 void DevToolsUIBindings::RequestFileSystems() {
578 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
579 file_helper_->RequestFileSystems(base::Bind(
580 &DevToolsUIBindings::FileSystemsLoaded, weak_factory_.GetWeakPtr()));
583 void DevToolsUIBindings::AddFileSystem() {
584 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
585 file_helper_->AddFileSystem(
586 base::Bind(&DevToolsUIBindings::FileSystemAdded,
587 weak_factory_.GetWeakPtr()),
588 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
589 weak_factory_.GetWeakPtr()));
592 void DevToolsUIBindings::RemoveFileSystem(const std::string& file_system_path) {
593 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
594 file_helper_->RemoveFileSystem(file_system_path);
595 base::StringValue file_system_path_value(file_system_path);
596 CallClientFunction("DevToolsAPI.fileSystemRemoved",
597 &file_system_path_value, NULL, NULL);
600 void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
601 const std::string& file_system_url) {
602 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
603 file_helper_->UpgradeDraggedFileSystemPermissions(
604 file_system_url,
605 base::Bind(&DevToolsUIBindings::FileSystemAdded,
606 weak_factory_.GetWeakPtr()),
607 base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
608 weak_factory_.GetWeakPtr()));
611 void DevToolsUIBindings::IndexPath(int index_request_id,
612 const std::string& file_system_path) {
613 DCHECK_CURRENTLY_ON(BrowserThread::UI);
614 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
615 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
616 IndexingDone(index_request_id, file_system_path);
617 return;
619 if (indexing_jobs_.count(index_request_id) != 0)
620 return;
621 indexing_jobs_[index_request_id] =
622 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
623 file_system_indexer_->IndexPath(
624 file_system_path,
625 Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated,
626 weak_factory_.GetWeakPtr(),
627 index_request_id,
628 file_system_path),
629 Bind(&DevToolsUIBindings::IndexingWorked,
630 weak_factory_.GetWeakPtr(),
631 index_request_id,
632 file_system_path),
633 Bind(&DevToolsUIBindings::IndexingDone,
634 weak_factory_.GetWeakPtr(),
635 index_request_id,
636 file_system_path)));
639 void DevToolsUIBindings::StopIndexing(int index_request_id) {
640 DCHECK_CURRENTLY_ON(BrowserThread::UI);
641 IndexingJobsMap::iterator it = indexing_jobs_.find(index_request_id);
642 if (it == indexing_jobs_.end())
643 return;
644 it->second->Stop();
645 indexing_jobs_.erase(it);
648 void DevToolsUIBindings::SearchInPath(int search_request_id,
649 const std::string& file_system_path,
650 const std::string& query) {
651 DCHECK_CURRENTLY_ON(BrowserThread::UI);
652 CHECK(web_contents_->GetURL().SchemeIs(content::kChromeDevToolsScheme));
653 if (!file_helper_->IsFileSystemAdded(file_system_path)) {
654 SearchCompleted(search_request_id,
655 file_system_path,
656 std::vector<std::string>());
657 return;
659 file_system_indexer_->SearchInPath(file_system_path,
660 query,
661 Bind(&DevToolsUIBindings::SearchCompleted,
662 weak_factory_.GetWeakPtr(),
663 search_request_id,
664 file_system_path));
667 void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string& message) {
668 delegate_->SetWhitelistedShortcuts(message);
671 void DevToolsUIBindings::ZoomIn() {
672 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN);
675 void DevToolsUIBindings::ZoomOut() {
676 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT);
679 void DevToolsUIBindings::ResetZoom() {
680 ui_zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
683 void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled) {
684 if (devices_updates_enabled_ == enabled)
685 return;
686 devices_updates_enabled_ = enabled;
687 if (enabled) {
688 remote_targets_handler_ = DevToolsTargetsUIHandler::CreateForAdb(
689 base::Bind(&DevToolsUIBindings::DevicesUpdated,
690 base::Unretained(this)),
691 profile_);
692 } else {
693 remote_targets_handler_.reset();
697 void DevToolsUIBindings::GetPreferences(const DispatchCallback& callback) {
698 const DictionaryValue* prefs =
699 profile_->GetPrefs()->GetDictionary(prefs::kDevToolsPreferences);
700 callback.Run(prefs);
703 void DevToolsUIBindings::SetPreference(const std::string& name,
704 const std::string& value) {
705 DictionaryPrefUpdate update(profile_->GetPrefs(),
706 prefs::kDevToolsPreferences);
707 update.Get()->SetStringWithoutPathExpansion(name, value);
710 void DevToolsUIBindings::RemovePreference(const std::string& name) {
711 DictionaryPrefUpdate update(profile_->GetPrefs(),
712 prefs::kDevToolsPreferences);
713 update.Get()->RemoveWithoutPathExpansion(name, nullptr);
716 void DevToolsUIBindings::ClearPreferences() {
717 DictionaryPrefUpdate update(profile_->GetPrefs(),
718 prefs::kDevToolsPreferences);
719 update.Get()->Clear();
722 void DevToolsUIBindings::SendMessageToBrowser(const std::string& message) {
723 if (agent_host_.get())
724 agent_host_->DispatchProtocolMessage(message);
727 void DevToolsUIBindings::RecordEnumeratedHistogram(const std::string& name,
728 int sample,
729 int boundary_value) {
730 if (!(boundary_value >= 0 && boundary_value <= 100 && sample >= 0 &&
731 sample < boundary_value)) {
732 // TODO(nick): Replace with chrome::bad_message::ReceivedBadMessage().
733 frontend_host_->BadMessageRecieved();
734 return;
736 // Each histogram name must follow a different code path in
737 // order to UMA_HISTOGRAM_ENUMERATION work correctly.
738 if (name == kDevToolsActionTakenHistogram)
739 UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value);
740 else if (name == kDevToolsPanelShownHistogram)
741 UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value);
742 else
743 frontend_host_->BadMessageRecieved();
746 void DevToolsUIBindings::SendJsonRequest(const DispatchCallback& callback,
747 const std::string& browser_id,
748 const std::string& url) {
749 if (!android_bridge_) {
750 callback.Run(nullptr);
751 return;
753 android_bridge_->SendJsonRequest(browser_id, url,
754 base::Bind(&DevToolsUIBindings::JsonReceived,
755 weak_factory_.GetWeakPtr(),
756 callback));
759 void DevToolsUIBindings::JsonReceived(const DispatchCallback& callback,
760 int result,
761 const std::string& message) {
762 if (result != net::OK) {
763 callback.Run(nullptr);
764 return;
766 base::StringValue message_value(message);
767 callback.Run(&message_value);
770 void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher* source) {
771 DCHECK(source);
772 PendingRequestsMap::iterator it = pending_requests_.find(source);
773 DCHECK(it != pending_requests_.end());
775 base::DictionaryValue response;
776 base::DictionaryValue* headers = new base::DictionaryValue();
777 net::HttpResponseHeaders* rh = source->GetResponseHeaders();
778 response.SetInteger("statusCode", rh ? rh->response_code() : 200);
779 response.Set("headers", headers);
781 void* iterator = NULL;
782 std::string name;
783 std::string value;
784 while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
785 headers->SetString(name, value);
787 it->second.Run(&response);
788 pending_requests_.erase(it);
789 delete source;
792 void DevToolsUIBindings::DeviceCountChanged(int count) {
793 base::FundamentalValue value(count);
794 CallClientFunction("DevToolsAPI.deviceCountUpdated", &value, NULL,
795 NULL);
798 void DevToolsUIBindings::DevicesUpdated(
799 const std::string& source,
800 const base::ListValue& targets) {
801 CallClientFunction("DevToolsAPI.devicesUpdated", &targets, NULL,
802 NULL);
805 void DevToolsUIBindings::FileSavedAs(const std::string& url) {
806 base::StringValue url_value(url);
807 CallClientFunction("DevToolsAPI.savedURL", &url_value, NULL, NULL);
810 void DevToolsUIBindings::CanceledFileSaveAs(const std::string& url) {
811 base::StringValue url_value(url);
812 CallClientFunction("DevToolsAPI.canceledSaveURL",
813 &url_value, NULL, NULL);
816 void DevToolsUIBindings::AppendedTo(const std::string& url) {
817 base::StringValue url_value(url);
818 CallClientFunction("DevToolsAPI.appendedToURL", &url_value, NULL,
819 NULL);
822 void DevToolsUIBindings::FileSystemsLoaded(
823 const std::vector<DevToolsFileHelper::FileSystem>& file_systems) {
824 base::ListValue file_systems_value;
825 for (size_t i = 0; i < file_systems.size(); ++i)
826 file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
827 CallClientFunction("DevToolsAPI.fileSystemsLoaded",
828 &file_systems_value, NULL, NULL);
831 void DevToolsUIBindings::FileSystemAdded(
832 const DevToolsFileHelper::FileSystem& file_system) {
833 scoped_ptr<base::StringValue> error_string_value(
834 new base::StringValue(std::string()));
835 scoped_ptr<base::DictionaryValue> file_system_value;
836 if (!file_system.file_system_path.empty())
837 file_system_value.reset(CreateFileSystemValue(file_system));
838 CallClientFunction("DevToolsAPI.fileSystemAdded",
839 error_string_value.get(), file_system_value.get(), NULL);
842 void DevToolsUIBindings::IndexingTotalWorkCalculated(
843 int request_id,
844 const std::string& file_system_path,
845 int total_work) {
846 DCHECK_CURRENTLY_ON(BrowserThread::UI);
847 base::FundamentalValue request_id_value(request_id);
848 base::StringValue file_system_path_value(file_system_path);
849 base::FundamentalValue total_work_value(total_work);
850 CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
851 &request_id_value, &file_system_path_value,
852 &total_work_value);
855 void DevToolsUIBindings::IndexingWorked(int request_id,
856 const std::string& file_system_path,
857 int worked) {
858 DCHECK_CURRENTLY_ON(BrowserThread::UI);
859 base::FundamentalValue request_id_value(request_id);
860 base::StringValue file_system_path_value(file_system_path);
861 base::FundamentalValue worked_value(worked);
862 CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value,
863 &file_system_path_value, &worked_value);
866 void DevToolsUIBindings::IndexingDone(int request_id,
867 const std::string& file_system_path) {
868 indexing_jobs_.erase(request_id);
869 DCHECK_CURRENTLY_ON(BrowserThread::UI);
870 base::FundamentalValue request_id_value(request_id);
871 base::StringValue file_system_path_value(file_system_path);
872 CallClientFunction("DevToolsAPI.indexingDone", &request_id_value,
873 &file_system_path_value, NULL);
876 void DevToolsUIBindings::SearchCompleted(
877 int request_id,
878 const std::string& file_system_path,
879 const std::vector<std::string>& file_paths) {
880 DCHECK_CURRENTLY_ON(BrowserThread::UI);
881 base::ListValue file_paths_value;
882 for (std::vector<std::string>::const_iterator it(file_paths.begin());
883 it != file_paths.end(); ++it) {
884 file_paths_value.AppendString(*it);
886 base::FundamentalValue request_id_value(request_id);
887 base::StringValue file_system_path_value(file_system_path);
888 CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value,
889 &file_system_path_value, &file_paths_value);
892 void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
893 const base::string16& message,
894 const InfoBarCallback& callback) {
895 DevToolsConfirmInfoBarDelegate::Create(delegate_->GetInfoBarService(),
896 callback, message);
899 void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
900 const extensions::ExtensionRegistry* registry =
901 extensions::ExtensionRegistry::Get(profile_->GetOriginalProfile());
902 if (!registry)
903 return;
905 base::ListValue results;
906 for (const scoped_refptr<const extensions::Extension>& extension :
907 registry->enabled_extensions()) {
908 if (extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
909 .is_empty())
910 continue;
911 base::DictionaryValue* extension_info = new base::DictionaryValue();
912 extension_info->Set(
913 "startPage",
914 new base::StringValue(extensions::chrome_manifest_urls::GetDevToolsPage(
915 extension.get()).spec()));
916 extension_info->Set("name", new base::StringValue(extension->name()));
917 extension_info->Set("exposeExperimentalAPIs",
918 new base::FundamentalValue(
919 extension->permissions_data()->HasAPIPermission(
920 extensions::APIPermission::kExperimental)));
921 results.Append(extension_info);
923 CallClientFunction("DevToolsAPI.addExtensions",
924 &results, NULL, NULL);
927 void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
928 delegate_.reset(delegate);
931 void DevToolsUIBindings::AttachTo(
932 const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
933 if (agent_host_.get())
934 Detach();
935 agent_host_ = agent_host;
936 agent_host_->AttachClient(this);
939 void DevToolsUIBindings::Reattach() {
940 DCHECK(agent_host_.get());
941 agent_host_->DetachClient();
942 agent_host_->AttachClient(this);
945 void DevToolsUIBindings::Detach() {
946 if (agent_host_.get())
947 agent_host_->DetachClient();
948 agent_host_ = NULL;
951 bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost* agent_host) {
952 return agent_host_.get() == agent_host;
955 void DevToolsUIBindings::CallClientFunction(const std::string& function_name,
956 const base::Value* arg1,
957 const base::Value* arg2,
958 const base::Value* arg3) {
959 std::string javascript = function_name + "(";
960 if (arg1) {
961 std::string json;
962 base::JSONWriter::Write(*arg1, &json);
963 javascript.append(json);
964 if (arg2) {
965 base::JSONWriter::Write(*arg2, &json);
966 javascript.append(", ").append(json);
967 if (arg3) {
968 base::JSONWriter::Write(*arg3, &json);
969 javascript.append(", ").append(json);
973 javascript.append(");");
974 web_contents_->GetMainFrame()->ExecuteJavaScript(
975 base::UTF8ToUTF16(javascript));
978 void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
979 // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
980 // arrives before the LoadCompleted event, thus it should not trigger the
981 // frontend load handling.
982 #if !defined(DEBUG_DEVTOOLS)
983 FrontendLoaded();
984 #endif
987 void DevToolsUIBindings::DidNavigateMainFrame() {
988 frontend_loaded_ = false;
991 void DevToolsUIBindings::FrontendLoaded() {
992 if (frontend_loaded_)
993 return;
994 frontend_loaded_ = true;
996 // Call delegate first - it seeds importants bit of information.
997 delegate_->OnLoadCompleted();
999 AddDevToolsExtensionsToClient();