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

@@ -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 {}