312 lines
10 KiB
Diff
312 lines
10 KiB
Diff
From 36d469443d498a2b24079776b6e24afbd9fa7248 Mon Sep 17 00:00:00 2001
|
|
From: Daisuke Akatsuka <daisuke@birchill.co.jp>
|
|
Date: Mon, 26 Oct 2020 05:30:44 +0000 (2020-10-26)
|
|
Subject: [PATCH] CVE-2020-26979
|
|
|
|
---
|
|
browser/base/content/utilityOverlay.js | 7 +-
|
|
browser/components/urlbar/UrlbarInput.jsm | 63 +++++++++--
|
|
.../urlbar/tests/browser/browser_enter.js | 100 ++++++++++++++++--
|
|
3 files changed, 147 insertions(+), 23 deletions(-)
|
|
|
|
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
|
|
index a23d6f05e6..c9d0ef8a71 100644
|
|
--- a/browser/base/content/utilityOverlay.js
|
|
+++ b/browser/base/content/utilityOverlay.js
|
|
@@ -695,8 +695,11 @@ function openLinkIn(url, where, params) {
|
|
}
|
|
break;
|
|
}
|
|
-
|
|
- if (!focusUrlBar && targetBrowser == w.gBrowser.selectedBrowser) {
|
|
+ if (
|
|
+ !params.avoidBrowserFocus &&
|
|
+ !focusUrlBar &&
|
|
+ targetBrowser == w.gBrowser.selectedBrowser
|
|
+ ) {
|
|
// Focus the content, but only if the browser used for the load is selected.
|
|
targetBrowser.focus();
|
|
}
|
|
diff --git a/browser/components/urlbar/UrlbarInput.jsm b/browser/components/urlbar/UrlbarInput.jsm
|
|
index 366149ec07..b9a916ca1b 100644
|
|
--- a/browser/components/urlbar/UrlbarInput.jsm
|
|
+++ b/browser/components/urlbar/UrlbarInput.jsm
|
|
@@ -16,6 +16,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|
ExtensionSearchHandler: "resource://gre/modules/ExtensionSearchHandler.jsm",
|
|
FormHistory: "resource://gre/modules/FormHistory.jsm",
|
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
|
+ PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
|
|
ReaderMode: "resource://gre/modules/ReaderMode.jsm",
|
|
Services: "resource://gre/modules/Services.jsm",
|
|
TopSiteAttribution: "resource:///modules/TopSiteAttribution.jsm",
|
|
@@ -474,7 +475,7 @@ class UrlbarInput {
|
|
isValidUrl = true;
|
|
} catch (ex) {}
|
|
if (isValidUrl) {
|
|
- this._loadURL(url, where, openParams);
|
|
+ this._loadURL(url, event, where, openParams);
|
|
return;
|
|
}
|
|
|
|
@@ -528,8 +529,8 @@ class UrlbarInput {
|
|
browser.lastLocationChange == lastLocationChange
|
|
) {
|
|
openParams.postData = postData.value;
|
|
- this._loadURL(uri.spec, where, openParams, null, browser);
|
|
- }
|
|
+ this._loadURL(uri.spec, event, where, openParams, null, browser);
|
|
+ }
|
|
}
|
|
});
|
|
// Don't add further handling here, the catch above is our last resort.
|
|
@@ -600,8 +601,8 @@ class UrlbarInput {
|
|
selIndex,
|
|
selType: "canonized",
|
|
});
|
|
- this._loadURL(this.value, where, openParams, browser);
|
|
- return;
|
|
+ this._loadURL(this.value, event, where, openParams, browser);
|
|
+ return;
|
|
}
|
|
|
|
let { url, postData } = UrlbarUtils.getUrlFromResult(result);
|
|
@@ -844,6 +845,7 @@ class UrlbarInput {
|
|
|
|
this._loadURL(
|
|
url,
|
|
+ event,
|
|
where,
|
|
openParams,
|
|
{
|
|
@@ -1757,6 +1759,8 @@ class UrlbarInput {
|
|
*
|
|
* @param {string} url
|
|
* The URL to open.
|
|
+ @param {Event} event
|
|
+ * The event that triggered to load the url.
|
|
* @param {string} openUILinkWhere
|
|
* Where we expect the result to be opened.
|
|
* @param {object} params
|
|
@@ -1778,6 +1782,7 @@ class UrlbarInput {
|
|
*/
|
|
_loadURL(
|
|
url,
|
|
+ event,
|
|
openUILinkWhere,
|
|
params,
|
|
resultDetails = null,
|
|
@@ -1835,12 +1840,19 @@ class UrlbarInput {
|
|
} else {
|
|
params.initiatingDoc = this.window.document;
|
|
}
|
|
+ if (event?.keyCode === KeyEvent.DOM_VK_RETURN) {
|
|
+ if (openUILinkWhere === "current") {
|
|
+ params.avoidBrowserFocus = true;
|
|
+ this._keyDownEnterDeferred?.resolve(browser);
|
|
+ }
|
|
+ }
|
|
|
|
// Focus the content area before triggering loads, since if the load
|
|
// occurs in a new tab, we want focus to be restored to the content
|
|
// area when the current tab is re-selected.
|
|
- browser.focus();
|
|
-
|
|
+ if (!params.avoidBrowserFocus) {
|
|
+ browser.focus();
|
|
+ }
|
|
if (openUILinkWhere != "current") {
|
|
this.handleRevert();
|
|
}
|
|
@@ -2059,7 +2071,14 @@ class UrlbarInput {
|
|
) {
|
|
this.window.UpdatePopupNotificationsVisibility();
|
|
}
|
|
-
|
|
+
|
|
+ // If user move the focus to another component while pressing Enter key,
|
|
+ // then keyup at that component, as we can't get the event, clear the promise.
|
|
+ if (this._keyDownEnterDeferred) {
|
|
+ this._keyDownEnterDeferred.resolve();
|
|
+ this._keyDownEnterDeferred = null;
|
|
+ }
|
|
+
|
|
Services.obs.notifyObservers(null, "urlbar-blur");
|
|
}
|
|
|
|
@@ -2376,6 +2395,12 @@ class UrlbarInput {
|
|
}
|
|
|
|
_on_keydown(event) {
|
|
+ if (event.keyCode === KeyEvent.DOM_VK_RETURN) {
|
|
+ if (this._keyDownEnterDeferred) {
|
|
+ this._keyDownEnterDeferred.reject();
|
|
+ }
|
|
+ this._keyDownEnterDeferred = PromiseUtils.defer();
|
|
+ }
|
|
// Due to event deferring, it's possible preventDefault() won't be invoked
|
|
// soon enough to actually prevent some of the default behaviors, thus we
|
|
// have to handle the event "twice". This first immediate call passes false
|
|
@@ -2391,9 +2416,27 @@ class UrlbarInput {
|
|
this.controller.handleKeyNavigation(event);
|
|
});
|
|
}
|
|
+
|
|
+ async _on_keyup(event) {
|
|
+ if (
|
|
+ event.keyCode === KeyEvent.DOM_VK_RETURN &&
|
|
+ this._keyDownEnterDeferred
|
|
+ ) {
|
|
+ try {
|
|
+ const loadingBrowser = await this._keyDownEnterDeferred.promise;
|
|
+ // Ensure the selected browser didn't change in the meanwhile.
|
|
+ if (this.window.gBrowser.selectedBrowser === loadingBrowser) {
|
|
+ loadingBrowser.focus();
|
|
+ }
|
|
+ } catch (ex) {
|
|
+ // Not all the Enter actions in the urlbar will cause a navigation, then it
|
|
+ // is normal for this to be rejected.
|
|
+ }
|
|
+ this._keyDownEnterDeferred = null;
|
|
+ return;
|
|
+ }
|
|
|
|
- _on_keyup(event) {
|
|
- this._toggleActionOverride(event);
|
|
+ this._toggleActionOverride(event);
|
|
}
|
|
|
|
_on_compositionstart(event) {
|
|
diff --git a/browser/components/urlbar/tests/browser/browser_enter.js b/browser/components/urlbar/tests/browser/browser_enter.js
|
|
index 3eb6df89c7..6d89815345 100644
|
|
--- a/browser/components/urlbar/tests/browser/browser_enter.js
|
|
+++ b/browser/components/urlbar/tests/browser/browser_enter.js
|
|
@@ -6,6 +6,20 @@
|
|
const TEST_VALUE = "example.com/\xF7?\xF7";
|
|
const START_VALUE = "example.com/%C3%B7?%C3%B7";
|
|
|
|
+add_task(async function setup() {
|
|
+ const engine = await SearchTestUtils.promiseNewSearchEngine(
|
|
+ getRootDirectory(gTestPath) + "searchSuggestionEngine.xml"
|
|
+ );
|
|
+
|
|
+ const defaultEngine = Services.search.defaultEngine;
|
|
+ Services.search.defaultEngine = engine;
|
|
+
|
|
+ registerCleanupFunction(async function() {
|
|
+ Services.search.defaultEngine = defaultEngine;
|
|
+ });
|
|
+});
|
|
+
|
|
+
|
|
add_task(async function returnKeypress() {
|
|
info("Simple return keypress");
|
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, START_VALUE);
|
|
@@ -82,12 +96,7 @@ add_task(async function altGrReturnKeypress() {
|
|
|
|
add_task(async function searchOnEnterNoPick() {
|
|
info("Search on Enter without picking a urlbar result");
|
|
- let engine = await SearchTestUtils.promiseNewSearchEngine(
|
|
- getRootDirectory(gTestPath) + "searchSuggestionEngine.xml"
|
|
- );
|
|
- let defaultEngine = Services.search.defaultEngine;
|
|
- Services.search.defaultEngine = engine;
|
|
- // Why is BrowserTestUtils.openNewForegroundTab not causing the bug?
|
|
+ // Why is BrowserTestUtils.openNewForegroundTab not causing the bug?
|
|
let promiseTabOpened = BrowserTestUtils.waitForEvent(
|
|
gBrowser.tabContainer,
|
|
"TabOpen"
|
|
@@ -95,11 +104,7 @@ add_task(async function searchOnEnterNoPick() {
|
|
EventUtils.synthesizeMouseAtCenter(gBrowser.tabContainer.newTabButton, {});
|
|
let openEvent = await promiseTabOpened;
|
|
let tab = openEvent.target;
|
|
- registerCleanupFunction(async function() {
|
|
- Services.search.defaultEngine = defaultEngine;
|
|
- BrowserTestUtils.removeTab(tab);
|
|
- });
|
|
-
|
|
+
|
|
let loadPromise = BrowserTestUtils.browserLoaded(
|
|
gBrowser.selectedBrowser,
|
|
false,
|
|
@@ -120,4 +125,77 @@ add_task(async function searchOnEnterNoPick() {
|
|
gURLBar.untrimmedValue,
|
|
"The location should have changed"
|
|
);
|
|
+
|
|
+ // Cleanup.
|
|
+ BrowserTestUtils.removeTab(tab);
|
|
+});
|
|
+
|
|
+add_task(async function searchOnEnterSoon() {
|
|
+ info("Search on Enter as soon as typing a char");
|
|
+ const tab = await BrowserTestUtils.openNewForegroundTab(
|
|
+ gBrowser,
|
|
+ START_VALUE
|
|
+ );
|
|
+
|
|
+ const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
|
+ const onBeforeUnload = SpecialPowers.spawn(
|
|
+ gBrowser.selectedBrowser,
|
|
+ [],
|
|
+ () => {
|
|
+ return new Promise(resolve => {
|
|
+ content.window.addEventListener("beforeunload", () => {
|
|
+ resolve();
|
|
+ });
|
|
+ });
|
|
+ }
|
|
+ );
|
|
+ const onResult = SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
|
+ return new Promise(resolve => {
|
|
+ content.window.addEventListener("keyup", () => {
|
|
+ resolve("keyup");
|
|
+ });
|
|
+ content.window.addEventListener("unload", () => {
|
|
+ resolve("unload");
|
|
+ });
|
|
+ });
|
|
+ });
|
|
+
|
|
+ // Focus on the input field in urlbar.
|
|
+ EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
|
|
+ const ownerDocument = gBrowser.selectedBrowser.ownerDocument;
|
|
+ is(
|
|
+ ownerDocument.activeElement,
|
|
+ gURLBar.inputField,
|
|
+ "The input field in urlbar has focus"
|
|
+ );
|
|
+
|
|
+ info("Keydown a char and Enter");
|
|
+ EventUtils.synthesizeKey("x", { type: "keydown" });
|
|
+ EventUtils.synthesizeKey("KEY_Enter", { type: "keydown" });
|
|
+
|
|
+ // Wait for beforeUnload event in the content.
|
|
+ await onBeforeUnload;
|
|
+ is(
|
|
+ ownerDocument.activeElement,
|
|
+ gURLBar.inputField,
|
|
+ "The input field in urlbar still has focus"
|
|
+ );
|
|
+
|
|
+ // Keyup both key as soon as beforeUnload event happens.
|
|
+ EventUtils.synthesizeKey("x", { type: "keyup" });
|
|
+ EventUtils.synthesizeKey("KEY_Enter", { type: "keyup" });
|
|
+
|
|
+ // Wait for moving the focus.
|
|
+ await TestUtils.waitForCondition(
|
|
+ () => ownerDocument.activeElement === gBrowser.selectedBrowser
|
|
+ );
|
|
+ info("The focus is moved to the browser");
|
|
+
|
|
+ // Check whether keyup event is not captured before unload event happens.
|
|
+ const result = await onResult;
|
|
+ is(result, "unload", "Keyup event is not captured.");
|
|
+
|
|
+ // Cleanup.
|
|
+ await onLoad;
|
|
+ BrowserTestUtils.removeTab(tab);
|
|
});
|
|
--
|
|
2.27.0
|
|
|