| // 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/page_handler.h" |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/web_applications/web_app_helpers.h" |
| #include "components/subresource_filter/content/browser/devtools_interaction_tracker.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/manifest/manifest_util.h" |
| #include "ui/gfx/image/image.h" |
| |
| #if BUILDFLAG(ENABLE_PRINTING) |
| #include "components/printing/browser/print_to_pdf/pdf_print_utils.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_PRINTING) |
| template <typename T> |
| absl::optional<T> OptionalFromMaybe(const protocol::Maybe<T>& maybe) { |
| return maybe.isJust() ? absl::optional<T>(maybe.fromJust()) : absl::nullopt; |
| } |
| #endif |
| |
| PageHandler::PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host, |
| content::WebContents* web_contents, |
| protocol::UberDispatcher* dispatcher) |
| : agent_host_(agent_host), web_contents_(web_contents->GetWeakPtr()) { |
| protocol::Page::Dispatcher::wire(dispatcher, this); |
| } |
| |
| PageHandler::~PageHandler() { |
| ToggleAdBlocking(/* enabled= */ false); |
| } |
| |
| void PageHandler::ToggleAdBlocking(bool enabled) { |
| if (!web_contents_) |
| return; |
| |
| // Create the DevtoolsInteractionTracker lazily (note that this call is a |
| // no-op if the object was already created). |
| subresource_filter::DevtoolsInteractionTracker::CreateForWebContents( |
| web_contents_.get()); |
| |
| subresource_filter::DevtoolsInteractionTracker::FromWebContents( |
| web_contents_.get()) |
| ->ToggleForceActivation(enabled); |
| } |
| |
| protocol::Response PageHandler::Enable() { |
| enabled_ = true; |
| // Do not mark the command as handled. Let it fall through instead, so that |
| // the handler in content gets a chance to process the command. |
| return protocol::Response::FallThrough(); |
| } |
| |
| protocol::Response PageHandler::Disable() { |
| enabled_ = false; |
| ToggleAdBlocking(false /* enable */); |
| // Do not mark the command as handled. Let it fall through instead, so that |
| // the handler in content gets a chance to process the command. |
| return protocol::Response::FallThrough(); |
| } |
| |
| protocol::Response PageHandler::SetAdBlockingEnabled(bool enabled) { |
| if (!enabled_) |
| return protocol::Response::ServerError("Page domain is disabled."); |
| ToggleAdBlocking(enabled); |
| return protocol::Response::Success(); |
| } |
| |
| void PageHandler::GetInstallabilityErrors( |
| std::unique_ptr<GetInstallabilityErrorsCallback> callback) { |
| auto errors = std::make_unique<protocol::Array<std::string>>(); |
| webapps::InstallableManager* manager = |
| web_contents_ |
| ? webapps::InstallableManager::FromWebContents(web_contents_.get()) |
| : nullptr; |
| if (!manager) { |
| callback->sendFailure( |
| protocol::Response::ServerError("Unable to fetch errors for target")); |
| return; |
| } |
| manager->GetAllErrors(base::BindOnce(&PageHandler::GotInstallabilityErrors, |
| std::move(callback))); |
| } |
| |
| // static |
| void PageHandler::GotInstallabilityErrors( |
| std::unique_ptr<GetInstallabilityErrorsCallback> callback, |
| std::vector<content::InstallabilityError> installability_errors) { |
| auto result_installability_errors = |
| std::make_unique<protocol::Array<protocol::Page::InstallabilityError>>(); |
| for (const auto& installability_error : installability_errors) { |
| auto installability_error_arguments = std::make_unique< |
| protocol::Array<protocol::Page::InstallabilityErrorArgument>>(); |
| for (const auto& error_argument : |
| installability_error.installability_error_arguments) { |
| installability_error_arguments->emplace_back( |
| protocol::Page::InstallabilityErrorArgument::Create() |
| .SetName(error_argument.name) |
| .SetValue(error_argument.value) |
| .Build()); |
| } |
| result_installability_errors->emplace_back( |
| protocol::Page::InstallabilityError::Create() |
| .SetErrorId(installability_error.error_id) |
| .SetErrorArguments(std::move(installability_error_arguments)) |
| .Build()); |
| } |
| callback->sendSuccess(std::move(result_installability_errors)); |
| } |
| |
| void PageHandler::GetManifestIcons( |
| std::unique_ptr<GetManifestIconsCallback> callback) { |
| webapps::InstallableManager* manager = |
| web_contents_ |
| ? webapps::InstallableManager::FromWebContents(web_contents_.get()) |
| : nullptr; |
| |
| if (!manager) { |
| callback->sendFailure( |
| protocol::Response::ServerError("Unable to fetch icons for target")); |
| return; |
| } |
| |
| manager->GetPrimaryIcon( |
| base::BindOnce(&PageHandler::GotManifestIcons, std::move(callback))); |
| } |
| |
| void PageHandler::GotManifestIcons( |
| std::unique_ptr<GetManifestIconsCallback> callback, |
| const SkBitmap* primary_icon) { |
| protocol::Maybe<protocol::Binary> primaryIconAsBinary; |
| |
| if (primary_icon && !primary_icon->empty()) { |
| primaryIconAsBinary = std::move(protocol::Binary::fromRefCounted( |
| gfx::Image::CreateFrom1xBitmap(*primary_icon).As1xPNGBytes())); |
| } |
| |
| callback->sendSuccess(std::move(primaryIconAsBinary)); |
| } |
| |
| void PageHandler::PrintToPDF(protocol::Maybe<bool> landscape, |
| protocol::Maybe<bool> display_header_footer, |
| protocol::Maybe<bool> print_background, |
| protocol::Maybe<double> scale, |
| protocol::Maybe<double> paper_width, |
| protocol::Maybe<double> paper_height, |
| protocol::Maybe<double> margin_top, |
| protocol::Maybe<double> margin_bottom, |
| protocol::Maybe<double> margin_left, |
| protocol::Maybe<double> margin_right, |
| protocol::Maybe<protocol::String> page_ranges, |
| protocol::Maybe<bool> ignore_invalid_page_ranges, |
| protocol::Maybe<protocol::String> header_template, |
| protocol::Maybe<protocol::String> footer_template, |
| protocol::Maybe<bool> prefer_css_page_size, |
| protocol::Maybe<protocol::String> transfer_mode, |
| std::unique_ptr<PrintToPDFCallback> callback) { |
| DCHECK(callback); |
| |
| #if BUILDFLAG(ENABLE_PRINTING) |
| if (!web_contents_) { |
| callback->sendFailure( |
| protocol::Response::ServerError("No web contents to print")); |
| return; |
| } |
| |
| absl::variant<printing::mojom::PrintPagesParamsPtr, std::string> |
| print_pages_params = print_to_pdf::GetPrintPagesParams( |
| web_contents_->GetMainFrame()->GetLastCommittedURL(), |
| OptionalFromMaybe<bool>(landscape), |
| OptionalFromMaybe<bool>(display_header_footer), |
| OptionalFromMaybe<bool>(print_background), |
| OptionalFromMaybe<double>(scale), |
| OptionalFromMaybe<double>(paper_width), |
| OptionalFromMaybe<double>(paper_height), |
| OptionalFromMaybe<double>(margin_top), |
| OptionalFromMaybe<double>(margin_bottom), |
| OptionalFromMaybe<double>(margin_left), |
| OptionalFromMaybe<double>(margin_right), |
| OptionalFromMaybe<std::string>(header_template), |
| OptionalFromMaybe<std::string>(footer_template), |
| OptionalFromMaybe<bool>(prefer_css_page_size)); |
| if (absl::holds_alternative<std::string>(print_pages_params)) { |
| callback->sendFailure(protocol::Response::InvalidParams( |
| absl::get<std::string>(print_pages_params))); |
| return; |
| } |
| |
| DCHECK(absl::holds_alternative<printing::mojom::PrintPagesParamsPtr>( |
| print_pages_params)); |
| |
| bool return_as_stream = |
| transfer_mode.fromMaybe("") == |
| protocol::Page::PrintToPDF::TransferModeEnum::ReturnAsStream; |
| |
| if (auto* print_manager = |
| print_to_pdf::PdfPrintManager::FromWebContents(web_contents_.get())) { |
| print_manager->PrintToPdf( |
| web_contents_->GetMainFrame(), page_ranges.fromMaybe(""), |
| ignore_invalid_page_ranges.fromMaybe(false), |
| std::move(absl::get<printing::mojom::PrintPagesParamsPtr>( |
| print_pages_params)), |
| base::BindOnce(&PageHandler::OnPDFCreated, |
| weak_ptr_factory_.GetWeakPtr(), return_as_stream, |
| std::move(callback))); |
| } else { |
| callback->sendFailure( |
| protocol::Response::ServerError("Printing is not available")); |
| } |
| #else |
| callback->sendFailure( |
| protocol::Response::ServerError("Printing is not enabled")); |
| #endif // BUILDFLAG(ENABLE_PRINTING) |
| } |
| |
| void PageHandler::GetAppId(std::unique_ptr<GetAppIdCallback> callback) { |
| webapps::InstallableManager* manager = |
| web_contents_ |
| ? webapps::InstallableManager::FromWebContents(web_contents_.get()) |
| : nullptr; |
| |
| if (!manager) { |
| callback->sendFailure( |
| protocol::Response::ServerError("Unable to fetch app id for target")); |
| return; |
| } |
| |
| webapps::InstallableParams params; |
| manager->GetData(params, base::BindOnce(&PageHandler::OnDidGetManifest, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void PageHandler::OnDidGetManifest(std::unique_ptr<GetAppIdCallback> callback, |
| const webapps::InstallableData& data) { |
| if (blink::IsEmptyManifest(data.manifest) || |
| !base::FeatureList::IsEnabled(blink::features::kWebAppEnableManifestId)) { |
| callback->sendSuccess(protocol::Maybe<protocol::String>(), |
| protocol::Maybe<protocol::String>()); |
| return; |
| } |
| absl::optional<std::string> id; |
| if (data.manifest.id.has_value()) { |
| id = base::UTF16ToUTF8(data.manifest.id.value()); |
| } |
| callback->sendSuccess( |
| web_app::GenerateAppIdUnhashed(id, data.manifest.start_url), |
| web_app::GenerateRecommendedId(data.manifest.start_url)); |
| } |
| |
| #if BUILDFLAG(ENABLE_PRINTING) |
| void PageHandler::OnPDFCreated( |
| bool return_as_stream, |
| std::unique_ptr<PrintToPDFCallback> callback, |
| print_to_pdf::PdfPrintManager::PrintResult print_result, |
| scoped_refptr<base::RefCountedMemory> data) { |
| if (print_result != print_to_pdf::PdfPrintManager::PRINT_SUCCESS) { |
| callback->sendFailure(protocol::Response::ServerError( |
| print_to_pdf::PdfPrintManager::PrintResultToString(print_result))); |
| return; |
| } |
| |
| if (return_as_stream) { |
| std::string handle = agent_host_->CreateIOStreamFromData(data); |
| callback->sendSuccess(protocol::Binary(), handle); |
| } else { |
| callback->sendSuccess(protocol::Binary::fromRefCounted(data), |
| protocol::Maybe<std::string>()); |
| } |
| } |
| #endif // BUILDFLAG(ENABLE_PRINTING) |