blob: 66876f6bb6f39217b7e34129534b4614ebb0a8f3 [file] [log] [blame]
// 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 <vector>
#include "base/memory/ref_counted_memory.h"
#include "base/task/post_task.h"
#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
#include "chrome/browser/devtools/devtools_dock_tile.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"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_png_rep.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,
const std::string& target_id)
: target_id_(target_id) {
// Dispatcher can be null in tests.
if (dispatcher)
protocol::Browser::Dispatcher::wire(dispatcher, this);
}
BrowserHandler::~BrowserHandler() = default;
Response BrowserHandler::GetWindowForTarget(
protocol::Maybe<std::string> target_id,
int* out_window_id,
std::unique_ptr<protocol::Browser::Bounds>* out_bounds) {
auto host =
content::DevToolsAgentHost::GetForId(target_id.fromMaybe(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();
}
protocol::Response BrowserHandler::SetDockTile(
protocol::Maybe<std::string> label,
protocol::Maybe<protocol::Binary> image) {
std::vector<gfx::ImagePNGRep> reps;
if (image.isJust())
reps.emplace_back(image.fromJust().bytes(), 1);
DevToolsDockTile::Update(label.fromMaybe(std::string()),
reps.size() ? gfx::Image(reps) : gfx::Image());
return Response::OK();
}
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);
}