| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/devtools/chrome_devtools_manager_delegate.h" |
| |
| #include <utility> |
| |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/devtools/chrome_devtools_session.h" |
| #include "chrome/browser/devtools/device/android_device_manager.h" |
| #include "chrome/browser/devtools/device/tcp_device_provider.h" |
| #include "chrome/browser/devtools/devtools_browser_context_manager.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "chrome/browser/devtools/protocol/target_handler.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/policy/developer_tools_policy_handler.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/browser_navigator.h" |
| #include "chrome/browser/ui/browser_navigator_params.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "components/guest_view/browser/guest_view_base.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/process_manager.h" |
| #include "extensions/common/manifest.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "base/command_line.h" |
| #include "chromeos/chromeos_switches.h" |
| #endif |
| |
| using content::DevToolsAgentHost; |
| |
| const char ChromeDevToolsManagerDelegate::kTypeApp[] = "app"; |
| const char ChromeDevToolsManagerDelegate::kTypeBackgroundPage[] = |
| "background_page"; |
| |
| namespace { |
| |
| bool GetExtensionInfo(content::WebContents* wc, |
| std::string* name, |
| std::string* type) { |
| Profile* profile = Profile::FromBrowserContext(wc->GetBrowserContext()); |
| if (!profile) |
| return false; |
| const extensions::Extension* extension = |
| extensions::ProcessManager::Get(profile)->GetExtensionForWebContents(wc); |
| if (!extension) |
| return false; |
| extensions::ExtensionHost* extension_host = |
| extensions::ProcessManager::Get(profile)->GetBackgroundHostForExtension( |
| extension->id()); |
| if (extension_host && extension_host->host_contents() == wc) { |
| *name = extension->name(); |
| *type = ChromeDevToolsManagerDelegate::kTypeBackgroundPage; |
| return true; |
| } |
| if (extension->is_hosted_app() || extension->is_legacy_packaged_app() || |
| extension->is_platform_app()) { |
| *name = extension->name(); |
| *type = ChromeDevToolsManagerDelegate::kTypeApp; |
| return true; |
| } |
| return false; |
| } |
| |
| ChromeDevToolsManagerDelegate* g_instance; |
| |
| } // namespace |
| |
| // static |
| ChromeDevToolsManagerDelegate* ChromeDevToolsManagerDelegate::GetInstance() { |
| return g_instance; |
| } |
| |
| ChromeDevToolsManagerDelegate::ChromeDevToolsManagerDelegate() { |
| DCHECK(!g_instance); |
| g_instance = this; |
| } |
| |
| ChromeDevToolsManagerDelegate::~ChromeDevToolsManagerDelegate() { |
| DCHECK(g_instance == this); |
| g_instance = nullptr; |
| } |
| |
| void ChromeDevToolsManagerDelegate::Inspect( |
| content::DevToolsAgentHost* agent_host) { |
| DevToolsWindow::OpenDevToolsWindow(agent_host, nullptr); |
| } |
| |
| void ChromeDevToolsManagerDelegate::HandleCommand( |
| DevToolsAgentHost* agent_host, |
| content::DevToolsAgentHostClient* client, |
| std::unique_ptr<base::DictionaryValue> command_dict, |
| const std::string& message, |
| NotHandledCallback callback) { |
| DCHECK(sessions_.find(client) != sessions_.end()); |
| sessions_[client]->HandleCommand(std::move(command_dict), message, |
| std::move(callback)); |
| } |
| |
| std::string ChromeDevToolsManagerDelegate::GetTargetType( |
| content::WebContents* web_contents) { |
| if (base::ContainsValue(AllTabContentses(), web_contents)) |
| return DevToolsAgentHost::kTypePage; |
| |
| std::string extension_name; |
| std::string extension_type; |
| if (!GetExtensionInfo(web_contents, &extension_name, &extension_type)) |
| return DevToolsAgentHost::kTypeOther; |
| return extension_type; |
| } |
| |
| std::string ChromeDevToolsManagerDelegate::GetTargetTitle( |
| content::WebContents* web_contents) { |
| std::string extension_name; |
| std::string extension_type; |
| if (!GetExtensionInfo(web_contents, &extension_name, &extension_type)) |
| return std::string(); |
| return extension_name; |
| } |
| |
| bool ChromeDevToolsManagerDelegate::AllowInspectingRenderFrameHost( |
| content::RenderFrameHost* rfh) { |
| Profile* profile = |
| Profile::FromBrowserContext(rfh->GetProcess()->GetBrowserContext()); |
| return AllowInspection(profile, extensions::ProcessManager::Get(profile) |
| ->GetExtensionForRenderFrameHost(rfh)); |
| } |
| |
| // static |
| bool ChromeDevToolsManagerDelegate::AllowInspection( |
| Profile* profile, |
| content::WebContents* web_contents) { |
| const extensions::Extension* extension = nullptr; |
| if (web_contents) { |
| extension = |
| extensions::ProcessManager::Get( |
| Profile::FromBrowserContext(web_contents->GetBrowserContext())) |
| ->GetExtensionForWebContents(web_contents); |
| } |
| return AllowInspection(profile, extension); |
| } |
| |
| // static |
| bool ChromeDevToolsManagerDelegate::AllowInspection( |
| Profile* profile, |
| const extensions::Extension* extension) { |
| #if defined(OS_CHROMEOS) |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(chromeos::switches::kForceDevToolsAvailable)) |
| return true; |
| #endif |
| |
| using Availability = policy::DeveloperToolsPolicyHandler::Availability; |
| Availability availability = |
| policy::DeveloperToolsPolicyHandler::GetDevToolsAvailability( |
| profile->GetPrefs()); |
| #if defined(OS_CHROMEOS) |
| // Do not create DevTools if it's disabled for primary profile. |
| if (Profile* primary_profile = ProfileManager::GetPrimaryUserProfile()) { |
| availability = |
| policy::DeveloperToolsPolicyHandler::GetMostRestrictiveAvailability( |
| availability, |
| policy::DeveloperToolsPolicyHandler::GetDevToolsAvailability( |
| primary_profile->GetPrefs())); |
| } |
| #endif |
| |
| switch (availability) { |
| case Availability::kDisallowed: |
| return false; |
| case Availability::kAllowed: |
| return true; |
| case Availability::kDisallowedForForceInstalledExtensions: |
| return !extension || |
| !extensions::Manifest::IsPolicyLocation(extension->location()); |
| default: |
| NOTREACHED() << "Unknown developer tools policy"; |
| return true; |
| } |
| } |
| |
| void ChromeDevToolsManagerDelegate::ClientAttached( |
| content::DevToolsAgentHost* agent_host, |
| content::DevToolsAgentHostClient* client) { |
| DCHECK(sessions_.find(client) == sessions_.end()); |
| sessions_[client] = |
| std::make_unique<ChromeDevToolsSession>(agent_host, client); |
| } |
| |
| void ChromeDevToolsManagerDelegate::ClientDetached( |
| content::DevToolsAgentHost* agent_host, |
| content::DevToolsAgentHostClient* client) { |
| sessions_.erase(client); |
| } |
| |
| scoped_refptr<DevToolsAgentHost> |
| ChromeDevToolsManagerDelegate::CreateNewTarget(const GURL& url) { |
| NavigateParams params(ProfileManager::GetLastUsedProfile(), url, |
| ui::PAGE_TRANSITION_AUTO_TOPLEVEL); |
| params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| Navigate(¶ms); |
| if (!params.navigated_or_inserted_contents) |
| return nullptr; |
| return DevToolsAgentHost::GetOrCreateFor( |
| params.navigated_or_inserted_contents); |
| } |
| |
| std::string ChromeDevToolsManagerDelegate::GetDiscoveryPageHTML() { |
| return ui::ResourceBundle::GetSharedInstance() |
| .GetRawDataResource(IDR_DEVTOOLS_DISCOVERY_PAGE_HTML) |
| .as_string(); |
| } |
| |
| std::vector<content::BrowserContext*> |
| ChromeDevToolsManagerDelegate::GetBrowserContexts() { |
| return DevToolsBrowserContextManager::GetInstance().GetBrowserContexts(); |
| } |
| |
| content::BrowserContext* |
| ChromeDevToolsManagerDelegate::GetDefaultBrowserContext() { |
| return DevToolsBrowserContextManager::GetInstance() |
| .GetDefaultBrowserContext(); |
| } |
| |
| content::BrowserContext* ChromeDevToolsManagerDelegate::CreateBrowserContext() { |
| return DevToolsBrowserContextManager::GetInstance().CreateBrowserContext(); |
| } |
| |
| void ChromeDevToolsManagerDelegate::DisposeBrowserContext( |
| content::BrowserContext* context, |
| DisposeCallback callback) { |
| DevToolsBrowserContextManager::GetInstance().DisposeBrowserContext( |
| context, std::move(callback)); |
| } |
| |
| bool ChromeDevToolsManagerDelegate::HasBundledFrontendResources() { |
| return true; |
| } |
| |
| void ChromeDevToolsManagerDelegate::DevicesAvailable( |
| const DevToolsDeviceDiscovery::CompleteDevices& devices) { |
| DevToolsAgentHost::List remote_targets; |
| for (const auto& complete : devices) { |
| for (const auto& browser : complete.second->browsers()) { |
| for (const auto& page : browser->pages()) |
| remote_targets.push_back(page->CreateTarget()); |
| } |
| } |
| remote_agent_hosts_.swap(remote_targets); |
| } |
| |
| void ChromeDevToolsManagerDelegate::UpdateDeviceDiscovery() { |
| RemoteLocations remote_locations; |
| for (const auto& it : sessions_) { |
| TargetHandler* target_handler = it.second->target_handler(); |
| if (!target_handler) |
| continue; |
| RemoteLocations& locations = target_handler->remote_locations(); |
| remote_locations.insert(locations.begin(), locations.end()); |
| } |
| |
| bool equals = remote_locations.size() == remote_locations_.size(); |
| if (equals) { |
| RemoteLocations::iterator it1 = remote_locations.begin(); |
| RemoteLocations::iterator it2 = remote_locations_.begin(); |
| while (it1 != remote_locations.end()) { |
| DCHECK(it2 != remote_locations_.end()); |
| if (!(*it1).Equals(*it2)) |
| equals = false; |
| ++it1; |
| ++it2; |
| } |
| DCHECK(it2 == remote_locations_.end()); |
| } |
| |
| if (equals) |
| return; |
| |
| if (remote_locations.empty()) { |
| device_discovery_.reset(); |
| remote_agent_hosts_.clear(); |
| } else { |
| if (!device_manager_) |
| device_manager_ = AndroidDeviceManager::Create(); |
| |
| AndroidDeviceManager::DeviceProviders providers; |
| providers.push_back(new TCPDeviceProvider(remote_locations)); |
| device_manager_->SetDeviceProviders(providers); |
| |
| device_discovery_.reset(new DevToolsDeviceDiscovery(device_manager_.get(), |
| base::Bind(&ChromeDevToolsManagerDelegate::DevicesAvailable, |
| base::Unretained(this)))); |
| } |
| remote_locations_.swap(remote_locations); |
| } |
| |
| void ChromeDevToolsManagerDelegate::ResetAndroidDeviceManagerForTesting() { |
| device_manager_.reset(); |
| |
| // We also need |device_discovery_| to go away because there may be a pending |
| // task using a raw pointer to the DeviceManager we just deleted. |
| device_discovery_.reset(); |
| } |