alloy: Fix link click navigation in PDF files (fixes issue #3143)

This change adds a minimal implementation of the |tabs.update| extension API and
modifies StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent to return a valid
|streamInfo.tabId| value as required by the navigateInCurrentTab implementation
in chrome/browser/resources/pdf/browser_api.js.
This commit is contained in:
Marshall Greenblatt 2021-06-16 15:54:26 -04:00
parent 1d02cd2be0
commit de66fb9406
8 changed files with 265 additions and 33 deletions

View File

@ -486,6 +486,8 @@ static_library("libcef_static") {
"libcef/browser/extensions/api/storage/sync_value_store_cache.h",
"libcef/browser/extensions/api/tabs/tabs_api.cc",
"libcef/browser/extensions/api/tabs/tabs_api.h",
"libcef/browser/extensions/alloy_extensions_util.cc",
"libcef/browser/extensions/alloy_extensions_util.h",
"libcef/browser/extensions/browser_extensions_util.cc",
"libcef/browser/extensions/browser_extensions_util.h",
"libcef/browser/extensions/browser_platform_delegate_background.cc",

View File

@ -0,0 +1,20 @@
// Copyright 2021 The Chromium Embedded Framework 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 "libcef/browser/extensions/alloy_extensions_util.h"
#include "libcef/browser/alloy/alloy_browser_host_impl.h"
namespace extensions {
namespace alloy {
int GetTabIdForWebContents(content::WebContents* web_contents) {
auto browser = AlloyBrowserHostImpl::GetBrowserForContents(web_contents);
if (!browser)
return -1;
return browser->GetIdentifier();
}
} // namespace alloy
} // namespace extensions

View File

@ -0,0 +1,21 @@
// Copyright 2021 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_ALLOY_EXTENSIONS_UTIL_H_
#define CEF_LIBCEF_BROWSER_EXTENSIONS_ALLOY_EXTENSIONS_UTIL_H_
namespace content {
class WebContents;
}
namespace extensions {
namespace alloy {
// Returns the tabId for |web_contents|, or -1 if not found.
int GetTabIdForWebContents(content::WebContents* web_contents);
} // namespace alloy
} // namespace extensions
#endif // CEF_LIBCEF_BROWSER_EXTENSIONS_ALLOY_EXTENSIONS_UTIL_H_

View File

@ -6,10 +6,15 @@
#include "libcef/browser/extensions/extension_web_contents_observer.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_instance.h"
#include "extensions/browser/extension_api_frame_id_map.h"
#include "extensions/browser/extension_zoom_request_client.h"
#include "extensions/common/error_utils.h"
@ -99,6 +104,148 @@ ExtensionFunction::ResponseAction TabsCreateFunction::Run() {
: NoArguments());
}
BaseAPIFunction::BaseAPIFunction() : cef_details_(this) {}
content::WebContents* BaseAPIFunction::GetWebContents(int tab_id) {
// Find a browser that we can access, or set |error_| and return nullptr.
CefRefPtr<AlloyBrowserHostImpl> browser =
cef_details_.GetBrowserForTabIdFirstTime(tab_id, &error_);
if (!browser)
return nullptr;
return browser->web_contents();
}
ExtensionFunction::ResponseAction TabsUpdateFunction::Run() {
std::unique_ptr<tabs::Update::Params> params(
tabs::Update::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
tab_id_ = params->tab_id ? *params->tab_id : -1;
content::WebContents* web_contents = GetWebContents(tab_id_);
if (!web_contents)
return RespondNow(Error(std::move(error_)));
web_contents_ = web_contents;
// TODO(rafaelw): handle setting remaining tab properties:
// -title
// -favIconUrl
// Navigate the tab to a new location if the url is different.
if (params->update_properties.url.get()) {
std::string updated_url = *params->update_properties.url;
if (!UpdateURL(updated_url, tab_id_, &error_))
return RespondNow(Error(std::move(error_)));
}
bool active = false;
// TODO(rafaelw): Setting |active| from js doesn't make much sense.
// Move tab selection management up to window.
if (params->update_properties.selected.get())
active = *params->update_properties.selected;
// The 'active' property has replaced 'selected'.
if (params->update_properties.active.get())
active = *params->update_properties.active;
if (active) {
// TODO: Activate the tab at |tab_id_|.
NOTIMPLEMENTED();
return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
}
if (params->update_properties.highlighted.get() &&
*params->update_properties.highlighted) {
// TODO: Highlight the tab at |tab_id_|.
NOTIMPLEMENTED();
return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
}
if (params->update_properties.pinned.get() &&
*params->update_properties.pinned) {
// TODO: Pin the tab at |tab_id_|.
NOTIMPLEMENTED();
return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
}
if (params->update_properties.muted.get()) {
// TODO: Mute/unmute the tab at |tab_id_|.
NOTIMPLEMENTED();
return RespondNow(Error(ErrorUtils::FormatErrorMessage(
tabs_constants::kCannotUpdateMuteCaptured,
base::NumberToString(tab_id_))));
}
if (params->update_properties.opener_tab_id.get()) {
int opener_id = *params->update_properties.opener_tab_id;
if (opener_id == tab_id_)
return RespondNow(Error("Cannot set a tab's opener to itself."));
// TODO: Set the opener for the tab at |tab_id_|.
NOTIMPLEMENTED();
return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
}
if (params->update_properties.auto_discardable.get()) {
// TODO: Set auto-discardable state for the tab at |tab_id_|.
NOTIMPLEMENTED();
}
return RespondNow(GetResult());
}
bool TabsUpdateFunction::UpdateURL(const std::string& url_string,
int tab_id,
std::string* error) {
GURL url;
if (!ExtensionTabUtil::PrepareURLForNavigation(url_string, extension(), &url,
error)) {
return false;
}
const bool is_javascript_scheme = url.SchemeIs(url::kJavaScriptScheme);
// JavaScript URLs are forbidden in chrome.tabs.update().
if (is_javascript_scheme) {
*error = tabs_constants::kJavaScriptUrlsNotAllowedInTabsUpdate;
return false;
}
content::NavigationController::LoadURLParams load_params(url);
// Treat extension-initiated navigations as renderer-initiated so that the URL
// does not show in the omnibox until it commits. This avoids URL spoofs
// since URLs can be opened on behalf of untrusted content.
load_params.is_renderer_initiated = true;
// All renderer-initiated navigations need to have an initiator origin.
load_params.initiator_origin = extension()->origin();
// |source_site_instance| needs to be set so that a renderer process
// compatible with |initiator_origin| is picked by Site Isolation.
load_params.source_site_instance = content::SiteInstance::CreateForURL(
web_contents_->GetBrowserContext(),
load_params.initiator_origin->GetURL());
// Marking the navigation as initiated via an API means that the focus
// will stay in the omnibox - see https://crbug.com/1085779.
load_params.transition_type = ui::PAGE_TRANSITION_FROM_API;
web_contents_->GetController().LoadURLWithParams(load_params);
DCHECK_EQ(url,
web_contents_->GetController().GetPendingEntry()->GetVirtualURL());
return true;
}
ExtensionFunction::ResponseValue TabsUpdateFunction::GetResult() {
if (!has_callback())
return NoArguments();
return ArgumentList(tabs::Get::Results::Create(*cef_details_.CreateTabObject(
AlloyBrowserHostImpl::GetBrowserForContents(web_contents_),
/*opener_browser_id=*/-1, /*active=*/true, tab_id_)));
}
ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
: cef_details_(this), execute_tab_id_(-1) {}
@ -246,18 +393,6 @@ bool TabsRemoveCSSFunction::ShouldRemoveCSS() const {
return true;
}
ZoomAPIFunction::ZoomAPIFunction() : cef_details_(this) {}
content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
// Find a browser that we can access, or set |error_| and return nullptr.
CefRefPtr<AlloyBrowserHostImpl> browser =
cef_details_.GetBrowserForTabIdFirstTime(tab_id, &error_);
if (!browser)
return nullptr;
return browser->web_contents();
}
ExtensionFunction::ResponseAction TabsSetZoomFunction::Run() {
std::unique_ptr<tabs::SetZoom::Params> params(
tabs::SetZoom::Params::Create(*args_));

View File

@ -42,6 +42,37 @@ class TabsCreateFunction : public ExtensionFunction {
const CefExtensionFunctionDetails cef_details_;
};
class BaseAPIFunction : public ExtensionFunction {
public:
BaseAPIFunction();
protected:
~BaseAPIFunction() override {}
// Gets the WebContents for |tab_id| if it is specified. Otherwise get the
// WebContents for the active tab in the current window. Calling this function
// may set |error_|.
content::WebContents* GetWebContents(int tab_id);
std::string error_;
const CefExtensionFunctionDetails cef_details_;
};
class TabsUpdateFunction : public BaseAPIFunction {
private:
~TabsUpdateFunction() override {}
ResponseAction Run() override;
bool UpdateURL(const std::string& url, int tab_id, std::string* error);
ResponseValue GetResult();
DECLARE_EXTENSION_FUNCTION("tabs.update", TABS_UPDATE)
int tab_id_ = -1;
content::WebContents* web_contents_ = nullptr;
};
// Implement API calls tabs.executeScript, tabs.insertCSS, and tabs.removeCSS.
class ExecuteCodeInTabFunction : public ExecuteCodeFunction {
public:
@ -114,7 +145,7 @@ class ZoomAPIFunction : public ExtensionFunction {
const CefExtensionFunctionDetails cef_details_;
};
class TabsSetZoomFunction : public ZoomAPIFunction {
class TabsSetZoomFunction : public BaseAPIFunction {
private:
~TabsSetZoomFunction() override {}
@ -123,7 +154,7 @@ class TabsSetZoomFunction : public ZoomAPIFunction {
DECLARE_EXTENSION_FUNCTION("tabs.setZoom", TABS_SETZOOM)
};
class TabsGetZoomFunction : public ZoomAPIFunction {
class TabsGetZoomFunction : public BaseAPIFunction {
private:
~TabsGetZoomFunction() override {}
@ -132,7 +163,7 @@ class TabsGetZoomFunction : public ZoomAPIFunction {
DECLARE_EXTENSION_FUNCTION("tabs.getZoom", TABS_GETZOOM)
};
class TabsSetZoomSettingsFunction : public ZoomAPIFunction {
class TabsSetZoomSettingsFunction : public BaseAPIFunction {
private:
~TabsSetZoomSettingsFunction() override {}
@ -141,7 +172,7 @@ class TabsSetZoomSettingsFunction : public ZoomAPIFunction {
DECLARE_EXTENSION_FUNCTION("tabs.setZoomSettings", TABS_SETZOOMSETTINGS)
};
class TabsGetZoomSettingsFunction : public ZoomAPIFunction {
class TabsGetZoomSettingsFunction : public BaseAPIFunction {
private:
~TabsGetZoomSettingsFunction() override {}

View File

@ -51,6 +51,7 @@ const char* const kSupportedAPIs[] = {
"tabs",
EXTENSION_FUNCTION_NAME(cefimpl::TabsGetFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsCreateFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsUpdateFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsExecuteScriptFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsInsertCSSFunction),
EXTENSION_FUNCTION_NAME(cefimpl::TabsRemoveCSSFunction),
@ -95,6 +96,7 @@ void ChromeFunctionRegistry::RegisterAll(ExtensionFunctionRegistry* registry) {
registry->RegisterFunction<cefimpl::TabsRemoveCSSFunction>();
registry->RegisterFunction<cefimpl::TabsGetFunction>();
registry->RegisterFunction<cefimpl::TabsCreateFunction>();
registry->RegisterFunction<cefimpl::TabsUpdateFunction>();
registry->RegisterFunction<cefimpl::TabsSetZoomFunction>();
registry->RegisterFunction<cefimpl::TabsGetZoomFunction>();
registry->RegisterFunction<cefimpl::TabsSetZoomSettingsFunction>();

View File

@ -318,24 +318,12 @@ base::DictionaryValue* CefExtensionFunctionDetails::OpenTab(
GURL url;
if (params.url.get()) {
std::string url_string = *params.url;
url = ExtensionTabUtil::ResolvePossiblyRelativeURL(url_string,
function()->extension());
if (!url.is_valid()) {
if (error_message) {
*error_message =
ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, url_string);
}
if (!ExtensionTabUtil::PrepareURLForNavigation(
url_string, function()->extension(), &url, error_message)) {
return nullptr;
}
}
// Don't let extensions crash the browser or renderers.
if (ExtensionTabUtil::IsKillURL(url)) {
if (error_message)
*error_message = keys::kNoCrashBrowserError;
return nullptr;
}
// Default to foreground for the new tab. The presence of 'active' property
// will override this default.
bool active = true;

View File

@ -1,5 +1,5 @@
diff --git chrome/browser/extensions/api/streams_private/streams_private_api.cc chrome/browser/extensions/api/streams_private/streams_private_api.cc
index 5c903a13a14ed..d385c6c0c95c4 100644
index 5c903a13a14ed..c85964a7bab17 100644
--- chrome/browser/extensions/api/streams_private/streams_private_api.cc
+++ chrome/browser/extensions/api/streams_private/streams_private_api.cc
@@ -6,6 +6,7 @@
@ -10,7 +10,18 @@ index 5c903a13a14ed..d385c6c0c95c4 100644
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_contents.h"
@@ -42,6 +43,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
@@ -18,6 +19,10 @@
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/common/manifest_handlers/mime_types_handler.h"
+#if BUILDFLAG(ENABLE_CEF)
+#include "cef/libcef/browser/extensions/alloy_extensions_util.h"
+#endif
+
namespace extensions {
void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
@@ -42,6 +47,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
if (!web_contents)
return;
@ -18,7 +29,7 @@ index 5c903a13a14ed..d385c6c0c95c4 100644
// If the request was for NoStatePrefetch, abort the prefetcher and do not
// continue. This is because plugins cancel NoStatePrefetch, see
// http://crbug.com/343590.
@@ -52,6 +54,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
@@ -52,6 +58,7 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
no_state_prefetch_contents->Destroy(prerender::FINAL_STATUS_DOWNLOAD);
return;
}
@ -26,6 +37,28 @@ index 5c903a13a14ed..d385c6c0c95c4 100644
auto* browser_context = web_contents->GetBrowserContext();
@@ -78,9 +85,18 @@ void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent(
// forms of zooming won't work).
// TODO(1042323): Present a coherent representation of a tab id for portal
// contents.
- int tab_id = web_contents->GetOuterWebContents()
- ? SessionID::InvalidValue().id()
- : ExtensionTabUtil::GetTabId(web_contents);
+ int tab_id;
+ if (web_contents->GetOuterWebContents()) {
+ tab_id = SessionID::InvalidValue().id();
+ } else
+#if BUILDFLAG(ENABLE_CEF)
+ if (cef::IsAlloyRuntimeEnabled()) {
+ tab_id = alloy::GetTabIdForWebContents(web_contents);
+ } else
+#endif // BUILDFLAG(ENABLE_CEF)
+ {
+ tab_id = ExtensionTabUtil::GetTabId(web_contents);
+ }
std::unique_ptr<StreamContainer> stream_container(
new StreamContainer(tab_id, embedded, handler_url, extension_id,
diff --git extensions/browser/extension_host.cc extensions/browser/extension_host.cc
index 7e38155c3b695..78768b28d8c99 100644
--- extensions/browser/extension_host.cc