summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzabolcs David <davidsz@inf.u-szeged.hu>2021-08-19 15:22:21 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2021-10-04 10:20:46 +0200
commit0ff9e0014bc32c3d13852fc4d92367bac22cb4c1 (patch)
tree28d4d3da2d65a2dbf7e19a037bcc2cd1dbcbc90a
parent7bfd8534f5f557517873734779502c315fe5270e (diff)
downloadqtwebengine-chromium-0ff9e0014bc32c3d13852fc4d92367bac22cb4c1.tar.gz
Fix navigation when clicking on links in a PDF
Implement a very limited version of chrome.tabs.update() JavaScript API with only one functionality: navigating the current WebContents when an URL update was requested. Task-number: QTBUG-95282 Change-Id: I9628262ed73aefb2ef53934e724444dd3378a9ad Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--chromium/chrome/browser/resources/pdf/browser_api.js2
-rw-r--r--chromium/extensions/common/api/_webengine_api_features.json6
-rw-r--r--chromium/qtwebengine/browser/extensions/api/BUILD.gn12
-rw-r--r--chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.cc222
-rw-r--r--chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.h77
-rw-r--r--chromium/qtwebengine/common/extensions/api/schema.gni1
-rw-r--r--chromium/qtwebengine/common/extensions/api/tabs.json112
7 files changed, 431 insertions, 1 deletions
diff --git a/chromium/chrome/browser/resources/pdf/browser_api.js b/chromium/chrome/browser/resources/pdf/browser_api.js
index f03bdb567a5..789c9c29586 100644
--- a/chromium/chrome/browser/resources/pdf/browser_api.js
+++ b/chromium/chrome/browser/resources/pdf/browser_api.js
@@ -90,7 +90,7 @@ export class BrowserApi {
* @param {string} url The URL to navigate the tab to.
*/
navigateInCurrentTab(url) {
- const tabId = this.getStreamInfo().tabId;
+ const tabId = 0;
// We need to use the tabs API to navigate because
// |window.location.href| cannot be used. This PDF extension is not loaded
// in the top level frame (it's embedded using MimeHandlerView). Using
diff --git a/chromium/extensions/common/api/_webengine_api_features.json b/chromium/extensions/common/api/_webengine_api_features.json
index b72dd74b4cf..e9b9bf83c2b 100644
--- a/chromium/extensions/common/api/_webengine_api_features.json
+++ b/chromium/extensions/common/api/_webengine_api_features.json
@@ -28,6 +28,12 @@
"dependencies": ["permission:enterprise.hardwarePlatform"],
"contexts": ["blessed_extension"]
},
+ "tabs": {
+ "channel": "stable",
+ "extension_types": ["extension", "legacy_packaged_app"],
+ "contexts": ["blessed_extension"],
+ "default_parent": true
+ },
"webrtcDesktopCapturePrivate": {
"dependencies": ["permission:webrtcDesktopCapturePrivate"],
"contexts": ["blessed_extension"]
diff --git a/chromium/qtwebengine/browser/extensions/api/BUILD.gn b/chromium/qtwebengine/browser/extensions/api/BUILD.gn
index d0fbd45d1e0..177159b64e4 100644
--- a/chromium/qtwebengine/browser/extensions/api/BUILD.gn
+++ b/chromium/qtwebengine/browser/extensions/api/BUILD.gn
@@ -17,6 +17,17 @@ source_set("resources_private") {
]
}
+source_set("tabs") {
+ sources = [
+ "tabs/tabs_api.cc",
+ "tabs/tabs_api.h",
+ ]
+
+ deps = [
+ "//content/public/browser",
+ ]
+}
+
source_set("webrtc_desktop_capture_private") {
sources = [
"webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc",
@@ -37,6 +48,7 @@ function_registration("api_registration") {
deps = [
":resources_private",
+ ":tabs",
":webrtc_desktop_capture_private",
"//extensions/common/api",
diff --git a/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.cc b/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.cc
new file mode 100644
index 00000000000..c2b27a3e022
--- /dev/null
+++ b/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.cc
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// based on //chrome/browser/extensions/api/tabs/tabs_api.cc
+// Copyright 2015 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 "qtwebengine/browser/extensions/api/tabs/tabs_api.h"
+#include "qtwebengine/common/extensions/api/tabs.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "chrome/common/url_constants.h"
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/url_formatter/url_fixer.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/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/permissions/permissions_data.h"
+
+using content::NavigationController;
+using content::OpenURLParams;
+using content::WebContents;
+
+namespace extensions {
+
+namespace tabs = api::tabs;
+
+static GURL ResolvePossiblyRelativeURL(const std::string& url_string,
+ const Extension* extension) {
+ GURL url = GURL(url_string);
+ if (!url.is_valid() && extension)
+ url = extension->GetResourceURL(url_string);
+
+ return url;
+}
+
+static bool IsKillURL(const GURL& url) {
+#if DCHECK_IS_ON()
+ // Caller should ensure that |url| is already "fixed up" by
+ // url_formatter::FixupURL, which (among many other things) takes care
+ // of rewriting about:kill into chrome://kill/.
+ if (url.SchemeIs(url::kAboutScheme))
+ DCHECK(url.IsAboutBlank() || url.IsAboutSrcdoc());
+#endif
+
+ static const char* const kill_hosts[] = {
+ chrome::kChromeUICrashHost, chrome::kChromeUIDelayedHangUIHost,
+ chrome::kChromeUIHangUIHost, chrome::kChromeUIKillHost,
+ chrome::kChromeUIQuitHost, chrome::kChromeUIRestartHost,
+ content::kChromeUIBrowserCrashHost, content::kChromeUIMemoryExhaustHost,
+ };
+
+ if (!url.SchemeIs(content::kChromeUIScheme))
+ return false;
+
+ return base::Contains(kill_hosts, url.host_piece());
+}
+
+static bool PrepareURLForNavigation(const std::string& url_string,
+ const Extension* extension,
+ GURL* return_url,
+ std::string* error) {
+ GURL url = ResolvePossiblyRelativeURL(url_string, extension);
+
+ // Ideally, the URL would only be "fixed" for user input (e.g. for URLs
+ // entered into the Omnibox), but some extensions rely on the legacy behavior
+ // where all navigations were subject to the "fixing". See also
+ // https://crbug.com/1145381.
+ url = url_formatter::FixupURL(url.spec(), "" /* = desired_tld */);
+
+ // Reject invalid URLs.
+ if (!url.is_valid()) {
+ *error = ErrorUtils::FormatErrorMessage("Invalid url: \"*\".", url_string);
+ return false;
+ }
+
+ // Don't let the extension crash the browser or renderers.
+ if (IsKillURL(url)) {
+ *error = "I'm sorry. I'm afraid I can't do that.";
+ return false;
+ }
+
+ if (url.SchemeIs(content::kChromeDevToolsScheme)) {
+ *error = "Cannot navigate to a devtools:// page without either the devtools or "
+ "debugger permission.";
+ return false;
+ }
+
+ return_url->Swap(&url);
+ return true;
+}
+
+TabsUpdateFunction::TabsUpdateFunction() : web_contents_(nullptr) {}
+
+ExtensionFunction::ResponseAction TabsUpdateFunction::Run() {
+ std::unique_ptr<tabs::Update::Params> params(
+ tabs::Update::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ int tab_id = -1;
+ std::string error;
+
+ web_contents_ = GetSenderWebContents();
+ if (!web_contents_) {
+ return RespondNow(Error("The specified target is not found."));
+ } else {
+ web_contents_ = guest_view::GuestViewBase::GetTopLevelWebContents(web_contents_);
+ }
+
+ // 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)));
+ }
+
+ return RespondNow(GetResult());
+}
+
+bool TabsUpdateFunction::UpdateURL(const std::string& url_string,
+ int tab_id,
+ std::string* error) {
+ GURL url;
+ if (!PrepareURLForNavigation(url_string, extension(), &url,
+ error)) {
+ return false;
+ }
+
+ const bool is_javascript_scheme = url.SchemeIs(url::kJavaScriptScheme);
+ UMA_HISTOGRAM_BOOLEAN("Extensions.ApiTabUpdateJavascript",
+ is_javascript_scheme);
+ // JavaScript URLs are forbidden in chrome.tabs.update().
+ if (is_javascript_scheme) {
+ *error = "JavaScript URLs are not allowed in chrome.tabs.update. Use "
+ "chrome.tabs.executeScript instead.";
+ return false;
+ }
+
+ 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() {
+ return NoArguments();
+}
+
+void TabsUpdateFunction::OnExecuteCodeFinished(
+ const std::string& error,
+ const GURL& url,
+ const base::ListValue& script_result) {
+ if (!error.empty()) {
+ Respond(Error(error));
+ return;
+ }
+
+ return Respond(GetResult());
+}
+
+} // namespace extensions
diff --git a/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.h b/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.h
new file mode 100644
index 00000000000..3c18c4ccb3a
--- /dev/null
+++ b/chromium/qtwebengine/browser/extensions/api/tabs/tabs_api.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// based on //chrome/browser/extensions/api/tabs/tabs_api.h
+// Copyright 2015 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.
+
+#ifndef QTWEBENGINE_BROWSER_EXTENSIONS_API_TABS_API_H_
+#define QTWEBENGINE_BROWSER_EXTENSIONS_API_TABS_API_H_
+
+#include "base/macros.h"
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+
+class TabsUpdateFunction : public ExtensionFunction {
+ public:
+ TabsUpdateFunction();
+
+ protected:
+ ~TabsUpdateFunction() override {}
+ bool UpdateURL(const std::string& url,
+ int tab_id,
+ std::string* error);
+ ResponseValue GetResult();
+
+ content::WebContents* web_contents_;
+
+ private:
+ ResponseAction Run() override;
+ void OnExecuteCodeFinished(const std::string& error,
+ const GURL& on_url,
+ const base::ListValue& script_result);
+
+ DECLARE_EXTENSION_FUNCTION("tabs.update", TABS_UPDATE)
+};
+
+} // namespace extensions
+
+#endif // QTWEBENGINE_BROWSER_EXTENSIONS_API_TABS_API_H_
diff --git a/chromium/qtwebengine/common/extensions/api/schema.gni b/chromium/qtwebengine/common/extensions/api/schema.gni
index 7c3ee705d3a..2db0e4c7dbd 100644
--- a/chromium/qtwebengine/common/extensions/api/schema.gni
+++ b/chromium/qtwebengine/common/extensions/api/schema.gni
@@ -1,5 +1,6 @@
webengine_extensions_api_schema_files_ = [
"resources_private.idl",
+ "tabs.json",
"webrtc_desktop_capture_private.idl",
]
diff --git a/chromium/qtwebengine/common/extensions/api/tabs.json b/chromium/qtwebengine/common/extensions/api/tabs.json
new file mode 100644
index 00000000000..fa0bfb4891a
--- /dev/null
+++ b/chromium/qtwebengine/common/extensions/api/tabs.json
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 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.
+
+[
+ {
+ "namespace": "tabs",
+ "description": "Use the <code>chrome.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
+ "types": [
+ {
+ "id": "Tab",
+ "type": "object",
+ "properties": {
+ "id": {"type": "integer", "minimum": 0, "description": "The ID of the tab. Tab IDs are unique within a browser session."},
+ "index": {"type": "integer", "minimum": 0, "description": "The zero-based index of the tab within its window."},
+ "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window the tab is contained within."},
+ "selected": {"type": "boolean", "description": "Whether the tab is selected.", "nodoc": true},
+ "highlighted": {"type": "boolean", "description": "Whether the tab is highlighted."},
+ "active": {"type": "boolean", "description": "Whether the tab is active in its window."},
+ "pinned": {"type": "boolean", "description": "Whether the tab is pinned."},
+ "url": {"type": "string", "description": "The URL the tab is displaying."},
+ "title": {"type": "string", "optional": true, "description": "The title of the tab. This may not be available if the tab is loading."},
+ "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This may not be available if the tab is loading."},
+ "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
+ "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."}
+ }
+ }
+ ],
+ "properties": {
+ "TAB_ID_NONE": {
+ "value": -1,
+ "description": "An ID that represents the absence of a browser tab."
+ }
+ },
+ "functions": [
+ {
+ "name": "update",
+ "type": "function",
+ "description": "Modifies the properties of a tab. Properties that are not specified in <var>updateProperties</var> are not modified.",
+ "parameters": [
+ {
+ "type": "integer",
+ "name": "tabId",
+ "minimum": 0,
+ "optional": true,
+ "description": "Defaults to the selected tab of the <a href='windows#current-window'>current window</a>."
+ },
+ {
+ "type": "object",
+ "name": "updateProperties",
+ "properties": {
+ "url": {
+ "type": "string",
+ "optional": true,
+ "description": "A URL to navigate the tab to. JavaScript URLs are not supported; use $(ref:tabs.executeScript) instead."
+ },
+ "active": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
+ },
+ "highlighted": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Adds or removes the tab from the current selection."
+ },
+ "selected": {
+ "deprecated": "Please use <em>highlighted</em>.",
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether the tab should be selected."
+ },
+ "pinned": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether the tab should be pinned."
+ },
+ "muted": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether the tab should be muted."
+ },
+ "openerTabId": {
+ "type": "integer",
+ "minimum": 0,
+ "optional": true,
+ "description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
+ },
+ "autoDiscardable": {
+ "type": "boolean",
+ "optional": true,
+ "description": "Whether the tab should be discarded automatically by the browser when resources are low."
+ }
+ }
+ }
+ ],
+ "returns_async": {
+ "name": "callback",
+ "optional": true,
+ "parameters": [
+ {
+ "name": "tab",
+ "$ref": "Tab",
+ "optional": true,
+ "description": "Details about the updated tab. The $(ref:tabs.Tab) object does not contain <code>url</code>, <code>pendingUrl</code>, <code>title</code>, and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested."
+ }
+ ]
+ }
+ }
+ ]
+ }
+]