| // Copyright 2017 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/protocol/browser_handler.h" |
| |
| #include <set> |
| |
| #include "base/task/post_task.h" |
| #include "chrome/browser/devtools/chrome_devtools_manager_delegate.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/permissions/permission_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| |
| using PermissionOverrides = std::set<content::PermissionType>; |
| using protocol::Maybe; |
| using protocol::Response; |
| |
| namespace { |
| |
| BrowserWindow* GetBrowserWindow(int window_id) { |
| for (auto* b : *BrowserList::GetInstance()) { |
| if (b->session_id().id() == window_id) |
| return b->window(); |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<protocol::Browser::Bounds> GetBrowserWindowBounds( |
| BrowserWindow* window) { |
| std::string window_state = "normal"; |
| if (window->IsMinimized()) |
| window_state = "minimized"; |
| if (window->IsMaximized()) |
| window_state = "maximized"; |
| if (window->IsFullscreen()) |
| window_state = "fullscreen"; |
| |
| gfx::Rect bounds; |
| if (window->IsMinimized()) |
| bounds = window->GetRestoredBounds(); |
| else |
| bounds = window->GetBounds(); |
| return protocol::Browser::Bounds::Create() |
| .SetLeft(bounds.x()) |
| .SetTop(bounds.y()) |
| .SetWidth(bounds.width()) |
| .SetHeight(bounds.height()) |
| .SetWindowState(window_state) |
| .Build(); |
| } |
| |
| Response FromProtocolPermissionType( |
| const protocol::Browser::PermissionType& type, |
| content::PermissionType* out_type) { |
| if (type == protocol::Browser::PermissionTypeEnum::Notifications) { |
| *out_type = content::PermissionType::NOTIFICATIONS; |
| } else if (type == protocol::Browser::PermissionTypeEnum::Geolocation) { |
| *out_type = content::PermissionType::GEOLOCATION; |
| } else if (type == |
| protocol::Browser::PermissionTypeEnum::ProtectedMediaIdentifier) { |
| *out_type = content::PermissionType::PROTECTED_MEDIA_IDENTIFIER; |
| } else if (type == protocol::Browser::PermissionTypeEnum::Midi) { |
| *out_type = content::PermissionType::MIDI; |
| } else if (type == protocol::Browser::PermissionTypeEnum::MidiSysex) { |
| *out_type = content::PermissionType::MIDI_SYSEX; |
| } else if (type == protocol::Browser::PermissionTypeEnum::DurableStorage) { |
| *out_type = content::PermissionType::DURABLE_STORAGE; |
| } else if (type == protocol::Browser::PermissionTypeEnum::AudioCapture) { |
| *out_type = content::PermissionType::AUDIO_CAPTURE; |
| } else if (type == protocol::Browser::PermissionTypeEnum::VideoCapture) { |
| *out_type = content::PermissionType::VIDEO_CAPTURE; |
| } else if (type == protocol::Browser::PermissionTypeEnum::BackgroundSync) { |
| *out_type = content::PermissionType::BACKGROUND_SYNC; |
| } else if (type == protocol::Browser::PermissionTypeEnum::Flash) { |
| *out_type = content::PermissionType::FLASH; |
| } else if (type == protocol::Browser::PermissionTypeEnum::Sensors) { |
| *out_type = content::PermissionType::SENSORS; |
| } else if (type == |
| protocol::Browser::PermissionTypeEnum::AccessibilityEvents) { |
| *out_type = content::PermissionType::ACCESSIBILITY_EVENTS; |
| } else if (type == protocol::Browser::PermissionTypeEnum::ClipboardRead) { |
| *out_type = content::PermissionType::CLIPBOARD_READ; |
| } else if (type == protocol::Browser::PermissionTypeEnum::ClipboardWrite) { |
| *out_type = content::PermissionType::CLIPBOARD_WRITE; |
| } else if (type == protocol::Browser::PermissionTypeEnum::PaymentHandler) { |
| *out_type = content::PermissionType::PAYMENT_HANDLER; |
| } else if (type == protocol::Browser::PermissionTypeEnum::BackgroundFetch) { |
| *out_type = content::PermissionType::BACKGROUND_FETCH; |
| } else { |
| return Response::InvalidParams("Unknown permission type: " + type); |
| } |
| return Response::OK(); |
| } |
| |
| } // namespace |
| |
| BrowserHandler::BrowserHandler(protocol::UberDispatcher* dispatcher) { |
| // Dispatcher can be null in tests. |
| if (dispatcher) |
| protocol::Browser::Dispatcher::wire(dispatcher, this); |
| } |
| |
| BrowserHandler::~BrowserHandler() = default; |
| |
| Response BrowserHandler::GetWindowForTarget( |
| const std::string& target_id, |
| int* out_window_id, |
| std::unique_ptr<protocol::Browser::Bounds>* out_bounds) { |
| auto host = content::DevToolsAgentHost::GetForId(target_id); |
| if (!host) |
| return Response::Error("No target with given id"); |
| content::WebContents* web_contents = host->GetWebContents(); |
| if (!web_contents) |
| return Response::Error("No web contents in the target"); |
| |
| Browser* browser = nullptr; |
| for (auto* b : *BrowserList::GetInstance()) { |
| int tab_index = b->tab_strip_model()->GetIndexOfWebContents(web_contents); |
| if (tab_index != TabStripModel::kNoTab) |
| browser = b; |
| } |
| if (!browser) |
| return Response::Error("Browser window not found"); |
| |
| BrowserWindow* window = browser->window(); |
| *out_window_id = browser->session_id().id(); |
| *out_bounds = GetBrowserWindowBounds(window); |
| return Response::OK(); |
| } |
| |
| Response BrowserHandler::GetWindowBounds( |
| int window_id, |
| std::unique_ptr<protocol::Browser::Bounds>* out_bounds) { |
| BrowserWindow* window = GetBrowserWindow(window_id); |
| if (!window) |
| return Response::Error("Browser window not found"); |
| |
| *out_bounds = GetBrowserWindowBounds(window); |
| return Response::OK(); |
| } |
| |
| Response BrowserHandler::Close() { |
| base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, |
| base::BindOnce([]() { chrome::AttemptExit(); })); |
| return Response::OK(); |
| } |
| |
| Response BrowserHandler::SetWindowBounds( |
| int window_id, |
| std::unique_ptr<protocol::Browser::Bounds> window_bounds) { |
| BrowserWindow* window = GetBrowserWindow(window_id); |
| if (!window) |
| return Response::Error("Browser window not found"); |
| gfx::Rect bounds = window->GetBounds(); |
| const bool set_bounds = window_bounds->HasLeft() || window_bounds->HasTop() || |
| window_bounds->HasWidth() || |
| window_bounds->HasHeight(); |
| if (set_bounds) { |
| bounds.set_x(window_bounds->GetLeft(bounds.x())); |
| bounds.set_y(window_bounds->GetTop(bounds.y())); |
| bounds.set_width(window_bounds->GetWidth(bounds.width())); |
| bounds.set_height(window_bounds->GetHeight(bounds.height())); |
| } |
| |
| const std::string window_state = window_bounds->GetWindowState("normal"); |
| if (set_bounds && window_state != "normal") { |
| return Response::Error( |
| "The 'minimized', 'maximized' and 'fullscreen' states cannot be " |
| "combined with 'left', 'top', 'width' or 'height'"); |
| } |
| |
| if (window_state == "fullscreen") { |
| if (window->IsMinimized()) { |
| return Response::Error( |
| "To make minimized window fullscreen, " |
| "restore it to normal state first."); |
| } |
| window->GetExclusiveAccessContext()->EnterFullscreen( |
| GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE); |
| } else if (window_state == "maximized") { |
| if (window->IsMinimized() || window->IsFullscreen()) { |
| return Response::Error( |
| "To maximize a minimized or fullscreen " |
| "window, restore it to normal state first."); |
| } |
| window->Maximize(); |
| } else if (window_state == "minimized") { |
| if (window->IsFullscreen()) { |
| return Response::Error( |
| "To minimize a fullscreen window, restore it to normal " |
| "state first."); |
| } |
| window->Minimize(); |
| } else if (window_state == "normal") { |
| if (window->IsFullscreen()) |
| window->GetExclusiveAccessContext()->ExitFullscreen(); |
| else if (window->IsMinimized()) |
| window->Show(); |
| else if (window->IsMaximized()) |
| window->Restore(); |
| else if (set_bounds) |
| window->SetBounds(bounds); |
| } else { |
| NOTREACHED(); |
| } |
| |
| return Response::OK(); |
| } |
| |
| Response BrowserHandler::Disable() { |
| for (auto& browser_context_id : contexts_with_overridden_permissions_) { |
| Profile* profile = nullptr; |
| Maybe<std::string> context_id = |
| browser_context_id == "" ? Maybe<std::string>() |
| : Maybe<std::string>(browser_context_id); |
| FindProfile(context_id, &profile); |
| if (profile) { |
| PermissionManager* permission_manager = PermissionManager::Get(profile); |
| permission_manager->ResetPermissionOverridesForDevTools(); |
| } |
| } |
| contexts_with_overridden_permissions_.clear(); |
| return Response::OK(); |
| } |
| |
| Response BrowserHandler::GrantPermissions( |
| const std::string& origin, |
| std::unique_ptr<protocol::Array<protocol::Browser::PermissionType>> |
| permissions, |
| Maybe<std::string> browser_context_id) { |
| Profile* profile = nullptr; |
| Response response = FindProfile(browser_context_id, &profile); |
| if (!response.isSuccess()) |
| return response; |
| |
| PermissionOverrides overrides; |
| for (size_t i = 0; i < permissions->length(); ++i) { |
| content::PermissionType type; |
| Response type_response = |
| FromProtocolPermissionType(permissions->get(i), &type); |
| if (!type_response.isSuccess()) |
| return type_response; |
| overrides.insert(type); |
| } |
| |
| PermissionManager* permission_manager = PermissionManager::Get(profile); |
| GURL url = GURL(origin).GetOrigin(); |
| permission_manager->SetPermissionOverridesForDevTools(url, |
| std::move(overrides)); |
| contexts_with_overridden_permissions_.insert( |
| browser_context_id.fromMaybe("")); |
| return Response::FallThrough(); |
| } |
| |
| Response BrowserHandler::ResetPermissions( |
| Maybe<std::string> browser_context_id) { |
| Profile* profile = nullptr; |
| Response response = FindProfile(browser_context_id, &profile); |
| if (!response.isSuccess()) |
| return response; |
| PermissionManager* permission_manager = PermissionManager::Get(profile); |
| permission_manager->ResetPermissionOverridesForDevTools(); |
| contexts_with_overridden_permissions_.erase(browser_context_id.fromMaybe("")); |
| return Response::FallThrough(); |
| } |
| |
| Response BrowserHandler::FindProfile( |
| const Maybe<std::string>& browser_context_id, |
| Profile** profile) { |
| auto* delegate = ChromeDevToolsManagerDelegate::GetInstance(); |
| if (!browser_context_id.isJust()) { |
| *profile = |
| Profile::FromBrowserContext(delegate->GetDefaultBrowserContext()); |
| if (*profile == nullptr) |
| return Response::Error("Browser context management is not supported."); |
| return Response::OK(); |
| } |
| |
| std::string context_id = browser_context_id.fromJust(); |
| for (auto* context : delegate->GetBrowserContexts()) { |
| if (context->UniqueId() == context_id) { |
| *profile = Profile::FromBrowserContext(context); |
| return Response::OK(); |
| } |
| } |
| return Response::InvalidParams("Failed to find browser context for id " + |
| context_id); |
| } |