| // Copyright 2016 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 "headless/lib/browser/headless_devtools_manager_delegate.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/devtools_frontend_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "headless/grit/headless_lib_resources.h" |
| #include "headless/lib/browser/headless_browser_context_impl.h" |
| #include "headless/lib/browser/headless_browser_impl.h" |
| #include "headless/lib/browser/headless_web_contents_impl.h" |
| #include "headless/public/devtools/domains/target.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| namespace headless { |
| |
| namespace { |
| const char kIdParam[] = "id"; |
| const char kResultParam[] = "result"; |
| const char kErrorParam[] = "error"; |
| const char kErrorCodeParam[] = "code"; |
| const char kErrorMessageParam[] = "message"; |
| |
| // JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object |
| enum Error { |
| kErrorInvalidParam = -32602, |
| kErrorServerError = -32000 |
| }; |
| |
| std::unique_ptr<base::DictionaryValue> CreateSuccessResponse( |
| int command_id, |
| std::unique_ptr<base::Value> result) { |
| if (!result) |
| result.reset(new base::DictionaryValue()); |
| |
| std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue()); |
| response->SetInteger(kIdParam, command_id); |
| response->Set(kResultParam, std::move(result)); |
| return response; |
| } |
| |
| std::unique_ptr<base::DictionaryValue> CreateErrorResponse( |
| int command_id, |
| int error_code, |
| const std::string& error_message) { |
| std::unique_ptr<base::DictionaryValue> error_object( |
| new base::DictionaryValue()); |
| error_object->SetInteger(kErrorCodeParam, error_code); |
| error_object->SetString(kErrorMessageParam, error_message); |
| |
| std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue()); |
| response->Set(kErrorParam, std::move(error_object)); |
| return response; |
| } |
| |
| std::unique_ptr<base::DictionaryValue> CreateInvalidParamResponse( |
| int command_id, |
| const std::string& param) { |
| return CreateErrorResponse( |
| command_id, kErrorInvalidParam, |
| base::StringPrintf("Missing or invalid '%s' parameter", param.c_str())); |
| } |
| } // namespace |
| |
| HeadlessDevToolsManagerDelegate::HeadlessDevToolsManagerDelegate( |
| base::WeakPtr<HeadlessBrowserImpl> browser) |
| : browser_(std::move(browser)) { |
| command_map_["Target.createTarget"] = |
| &HeadlessDevToolsManagerDelegate::CreateTarget; |
| command_map_["Target.closeTarget"] = |
| &HeadlessDevToolsManagerDelegate::CloseTarget; |
| command_map_["Target.createBrowserContext"] = |
| &HeadlessDevToolsManagerDelegate::CreateBrowserContext; |
| command_map_["Target.disposeBrowserContext"] = |
| &HeadlessDevToolsManagerDelegate::DisposeBrowserContext; |
| } |
| |
| HeadlessDevToolsManagerDelegate::~HeadlessDevToolsManagerDelegate() {} |
| |
| base::DictionaryValue* HeadlessDevToolsManagerDelegate::HandleCommand( |
| content::DevToolsAgentHost* agent_host, |
| base::DictionaryValue* command) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!browser_) |
| return nullptr; |
| |
| int id; |
| std::string method; |
| const base::DictionaryValue* params = nullptr; |
| if (!command->GetInteger("id", &id) || |
| !command->GetString("method", &method) || |
| !command->GetDictionary("params", ¶ms)) { |
| return nullptr; |
| } |
| auto find_it = command_map_.find(method); |
| if (find_it == command_map_.end()) |
| return nullptr; |
| CommandMemberFnPtr command_fn_ptr = find_it->second; |
| std::unique_ptr<base::DictionaryValue> cmd_result( |
| ((this)->*command_fn_ptr)(id, params)); |
| return cmd_result.release(); |
| } |
| |
| std::string HeadlessDevToolsManagerDelegate::GetDiscoveryPageHTML() { |
| return ResourceBundle::GetSharedInstance() |
| .GetRawDataResource(IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE) |
| .as_string(); |
| } |
| |
| std::string HeadlessDevToolsManagerDelegate::GetFrontendResource( |
| const std::string& path) { |
| return content::DevToolsFrontendHost::GetFrontendResource(path).as_string(); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| HeadlessDevToolsManagerDelegate::CreateTarget( |
| int command_id, |
| const base::DictionaryValue* params) { |
| std::string url; |
| std::string browser_context_id; |
| int width = browser_->options()->window_size.width(); |
| int height = browser_->options()->window_size.height(); |
| params->GetString("url", &url); |
| params->GetString("browserContextId", &browser_context_id); |
| params->GetInteger("width", &width); |
| params->GetInteger("height", &height); |
| |
| HeadlessBrowserContext* context = |
| browser_->GetBrowserContextForId(browser_context_id); |
| if (!browser_context_id.empty()) { |
| context = browser_->GetBrowserContextForId(browser_context_id); |
| if (!context) |
| return CreateInvalidParamResponse(command_id, "browserContextId"); |
| } else { |
| context = browser_->GetDefaultBrowserContext(); |
| if (!context) { |
| return CreateErrorResponse(command_id, kErrorServerError, |
| "You specified no |browserContextId|, but " |
| "there is no default browser context set on " |
| "HeadlessBrowser"); |
| } |
| } |
| |
| HeadlessWebContentsImpl* web_contents_impl = |
| HeadlessWebContentsImpl::From(context->CreateWebContentsBuilder() |
| .SetInitialURL(GURL(url)) |
| .SetWindowSize(gfx::Size(width, height)) |
| .Build()); |
| |
| std::unique_ptr<base::Value> result( |
| target::CreateTargetResult::Builder() |
| .SetTargetId(web_contents_impl->GetDevToolsAgentHostId()) |
| .Build() |
| ->Serialize()); |
| return CreateSuccessResponse(command_id, std::move(result)); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| HeadlessDevToolsManagerDelegate::CloseTarget( |
| int command_id, |
| const base::DictionaryValue* params) { |
| std::string target_id; |
| if (!params->GetString("targetId", &target_id)) |
| return CreateInvalidParamResponse(command_id, "targetId"); |
| HeadlessWebContents* web_contents = |
| browser_->GetWebContentsForDevToolsAgentHostId(target_id); |
| bool success = false; |
| if (web_contents) { |
| web_contents->Close(); |
| success = true; |
| } |
| std::unique_ptr<base::Value> result(target::CloseTargetResult::Builder() |
| .SetSuccess(success) |
| .Build() |
| ->Serialize()); |
| return CreateSuccessResponse(command_id, std::move(result)); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| HeadlessDevToolsManagerDelegate::CreateBrowserContext( |
| int command_id, |
| const base::DictionaryValue* params) { |
| HeadlessBrowserContext* browser_context = |
| browser_->CreateBrowserContextBuilder().Build(); |
| |
| std::unique_ptr<base::Value> result( |
| target::CreateBrowserContextResult::Builder() |
| .SetBrowserContextId(browser_context->Id()) |
| .Build() |
| ->Serialize()); |
| return CreateSuccessResponse(command_id, std::move(result)); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| HeadlessDevToolsManagerDelegate::DisposeBrowserContext( |
| int command_id, |
| const base::DictionaryValue* params) { |
| std::string browser_context_id; |
| if (!params->GetString("browserContextId", &browser_context_id)) |
| return CreateInvalidParamResponse(command_id, "browserContextId"); |
| HeadlessBrowserContext* context = |
| browser_->GetBrowserContextForId(browser_context_id); |
| |
| bool success = false; |
| if (context && context != browser_->GetDefaultBrowserContext() && |
| context->GetAllWebContents().empty()) { |
| success = true; |
| context->Close(); |
| } |
| |
| std::unique_ptr<base::Value> result( |
| target::DisposeBrowserContextResult::Builder() |
| .SetSuccess(success) |
| .Build() |
| ->Serialize()); |
| return CreateSuccessResponse(command_id, std::move(result)); |
| } |
| |
| } // namespace headless |