Bug 1303838 - Switch to existing target tab when clicking links. r=smaug

Merge "DOMServiceWorkerFocusClient" & "DOMWebNotificationClicked"
to "DOMWindowFocus" event. Utilize the event to switch tab when
loading links to an existing target tab.

MozReview-Commit-ID: Hd1NkVkrJA1

--HG--
extra : rebase_source : 745c0d66c3afd8e487c616891c0f10bd820da1fe
This commit is contained in:
Samael Wang 2017-03-23 15:59:01 +08:00
Родитель 5c092525e2
Коммит d19130f17a
12 изменённых файлов: 250 добавлений и 31 удалений

Просмотреть файл

@ -698,12 +698,8 @@ ContentLinkHandler.init(this);
// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
var pluginContent = new PluginContent(global);
addEventListener("DOMWebNotificationClicked", function(event) {
sendAsyncMessage("DOMWebNotificationClicked", {});
}, false);
addEventListener("DOMServiceWorkerFocusClient", function(event) {
sendAsyncMessage("DOMServiceWorkerFocusClient", {});
addEventListener("DOMWindowFocus", function(event) {
sendAsyncMessage("DOMWindowFocus", {});
}, false);
ContentWebRTC.init();

Просмотреть файл

@ -5007,8 +5007,7 @@
popup.openPopupAtScreen(event.screenX, event.screenY, true);
break;
}
case "DOMServiceWorkerFocusClient":
case "DOMWebNotificationClicked": {
case "DOMWindowFocus": {
let tab = this.getTabForBrowser(browser);
if (!tab)
return undefined;
@ -5240,8 +5239,7 @@
this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
this.mCurrentBrowser);
}
messageManager.addMessageListener("DOMWebNotificationClicked", this);
messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
messageManager.addMessageListener("DOMWindowFocus", this);
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
messageManager.addMessageListener("Browser:WindowCreated", this);

Просмотреть файл

@ -10199,6 +10199,19 @@ nsDocShell::InternalLoad(nsIURI* aURI,
// window will need to be made visible... For now,
// do nothing.
}
// Switch to target tab if we're currently focused window.
// Take loadDivertedInBackground into account so the behavior would be
// the same as how the tab first opened.
bool isTargetActive = false;
targetDocShell->GetIsActive(&isTargetActive);
if (mIsActive && !isTargetActive &&
!Preferences::GetBool("browser.tabs.loadDivertedInBackground", false)) {
if (NS_FAILED(nsContentUtils::DispatchFocusChromeEvent(
targetDocShell->GetWindow()))) {
return NS_ERROR_FAILURE;
}
}
}
// Else we ran out of memory, or were a popup and got blocked,

Просмотреть файл

@ -4127,7 +4127,7 @@ nsContentUtils::DispatchFocusChromeEvent(nsPIDOMWindowOuter* aWindow)
}
return DispatchChromeEvent(doc, aWindow,
NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
NS_LITERAL_STRING("DOMWindowFocus"),
true, true);
}

Просмотреть файл

@ -1256,7 +1256,7 @@ public:
bool *aDefaultAction = nullptr);
/**
* Helper function for dispatching a "DOMServiceWorkerFocusClient" event to
* Helper function for dispatching a "DOMWindowFocus" event to
* the chrome event handler of the given DOM Window. This has the effect
* of focusing the corresponding tab and bringing the browser window
* to the foreground.

Просмотреть файл

@ -6,6 +6,9 @@ support-files =
file_audioLoopInIframe.html
file_bug1011748_redirect.sjs
file_bug1011748_OK.sjs
file_bug1303838.html
file_bug1303838_target.html
file_bug1303838_with_iframe.html
file_messagemanager_unload.html
file_pluginAudio.html
file_use_counter_outer.html
@ -33,3 +36,4 @@ skip-if = true # Bug 1271028
[browser_use_counters.js]
[browser_bug1307747.js]
[browser_timeout_throttling_with_audio_playback.js]
[browser_bug1303838.js]

Просмотреть файл

@ -0,0 +1,162 @@
/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 1303838.
* Load a tab with some links, emulate link clicks and check if the
* browser would switch to the existing target tab opened by previous
* link click if loadDivertedInBackground is set to true.
*/
"use strict";
const BASE_URL = "http://mochi.test:8888/browser/dom/base/test/";
add_task(function*() {
yield* testLinkClick(false, false);
yield* testLinkClick(false, true);
yield* testLinkClick(true, false);
yield* testLinkClick(true, true);
});
function* testLinkClick(withFrame, loadDivertedInBackground) {
yield SpecialPowers.pushPrefEnv({"set": [["browser.tabs.loadDivertedInBackground", loadDivertedInBackground]]});
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
BASE_URL + (withFrame ? "file_bug1303838_with_iframe.html" : "file_bug1303838.html"));
is(gBrowser.tabs.length, 2, "check tabs.length");
is(gBrowser.selectedTab, tab, "check selectedTab");
info("Test normal links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
let [testTab] = yield clickLink(withFrame, "#link-1", tab.linkedBrowser);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#link-4", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground, 2);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
info("Test anchor links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#anchor-link-1", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#anchor-link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#anchor-link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
info("Test iframe links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#frame-link-1", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#frame-link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
if (!loadDivertedInBackground) {
yield BrowserTestUtils.switchTab(gBrowser, tab);
}
yield clickLink(withFrame, "#frame-link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
is(gBrowser.tabs.length, 3, "check tabs.length");
is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
yield BrowserTestUtils.removeTab(testTab);
yield BrowserTestUtils.removeTab(tab);
}
function clickLink(isFrame, linkId, browser, testBrowser, awaitTabSwitch = false, locationChangeNum = 1) {
let promises = [];
if (awaitTabSwitch) {
promises.push(waitForTabSwitch(gBrowser));
}
promises.push(testBrowser ?
waitForLocationChange(testBrowser, locationChangeNum) :
BrowserTestUtils.waitForNewTab(gBrowser));
promises.push(ContentTask.spawn(browser, [isFrame, linkId],
([contentIsFrame, contentLinkId]) => {
let doc = content.document;
if (contentIsFrame) {
let frame = content.document.getElementById("frame");
doc = frame.contentDocument;
}
info("Clicking " + contentLinkId);
doc.querySelector(contentLinkId).click();
}));
return Promise.all(promises);
}
function waitForTabSwitch(tabbrowser) {
info("Waiting for TabSwitch");
return new Promise(resolve => {
tabbrowser.addEventListener("TabSwitchDone", function onSwitch() {
info("TabSwitch done");
tabbrowser.removeEventListener("TabSwitchDone", onSwitch);
resolve(tabbrowser.selectedTab);
});
});
}
// We need a longer lifetime reference to ensure the listener is alive when
// location change occurs.
let locationChangeListener;
function waitForLocationChange(browser, locationChangeNum) {
info("Waiting for " + locationChangeNum + " LocationChange");
return new Promise(resolve => {
let seen = 0;
locationChangeListener = {
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
info("LocationChange: " + aLocation.spec);
if (++seen == locationChangeNum) {
browser.removeProgressListener(this);
resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
browser.addProgressListener(locationChangeListener);
});
}

Просмотреть файл

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<!--
Tests for tab switching on link clicks.
-->
<head>
<meta charset="utf-8">
<title>Tests for tab switching on link clicks.</title>
</head>
<body>
<a id="link-1" target="testTab" href="data:text/html;charset=utf-8,foo">Link 1</a><br>
<a id="link-2" target="testTab" href="data:text/html;charset=utf-8,bar">Link 2</a><br>
<a id="link-3" target="testTab" href="data:text/html;charset=utf-8,baz">Link 3</a><br>
<a id="link-4" target="testTab" href="file_bug1303838_target.html">Link 4</a><br>
<a id="anchor-link-1" target="testTab" href="file_bug1303838_target.html#foo">Anchor Link 1</a><br>
<a id="anchor-link-2" target="testTab" href="file_bug1303838_target.html#bar">Anchor Link 2</a><br>
<a id="anchor-link-3" target="testTab" href="file_bug1303838_target.html#baz">Anchor Link 3</a><br>
<a id="frame-link-1" target="testFrame" href="data:text/html;charset=utf-8,ifoo">Frame Link 1</a><br>
<a id="frame-link-2" target="testFrame" href="data:text/html;charset=utf-8,ibar">Frame Link 2</a><br>
<a id="frame-link-3" target="testFrame" href="data:text/html;charset=utf-8,ibaz">Frame Link 3</a><br>
</body>
</html>

Просмотреть файл

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<!--
Tests for tab switching on link clicks.
-->
<head>
<meta charset="utf-8">
<title>Tests for tab switching on link clicks.</title>
</head>
<body onload="setTimeout(loadTestFrame, 0);">
<div id="foo">Foo</div>
<div id="bar">Bar</div>
<div id="baz">Baz</div>
<iframe name="testFrame"></iframe>
<script>
function loadTestFrame() {
window.open("data:text/html;charset=utf-8,testFrame", "testFrame");
}
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<!--
Tests for tab switching on link clicks.
-->
<head>
<meta charset="utf-8">
<title>Tests for tab switching on link clicks.</title>
</head>
<body>
<iframe id="frame" src="file_bug1303838.html"></iframe>
</body>
</html>

Просмотреть файл

@ -322,14 +322,9 @@ public:
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
// Browser UI may use DOMWebNotificationClicked to focus the tab
// from which the event was dispatched.
nsContentUtils::DispatchChromeEvent(doc, mWindow->GetOuterWindow(),
NS_LITERAL_STRING("DOMWebNotificationClicked"),
true, true);
}
// Browser UI may use DOMWindowFocus to focus the tab
// from which the event was dispatched.
nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
return NS_OK;
}
@ -1500,14 +1495,9 @@ MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopi
bool doDefaultAction = notification->DispatchClickEvent();
if (doDefaultAction) {
nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
if (doc) {
// Browser UI may use DOMWebNotificationClicked to focus the tab
// from which the event was dispatched.
nsContentUtils::DispatchChromeEvent(doc, window->GetOuterWindow(),
NS_LITERAL_STRING("DOMWebNotificationClicked"),
true, true);
}
// Browser UI may use DOMWindowFocus to focus the tab
// from which the event was dispatched.
nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
}
} else if (!strcmp("alertfinished", aTopic)) {
notification->UnpersistNotification();

Просмотреть файл

@ -3629,7 +3629,7 @@ Tab.prototype = {
this.browser.addEventListener("pageshow", this, true);
this.browser.addEventListener("MozApplicationManifest", this, true);
this.browser.addEventListener("TabPreZombify", this, true);
this.browser.addEventListener("DOMServiceWorkerFocusClient", this, true);
this.browser.addEventListener("DOMWindowFocus", this, true);
// Note that the XBL binding is untrusted
this.browser.addEventListener("PluginBindingAttached", this, true, true);
@ -3745,7 +3745,7 @@ Tab.prototype = {
this.browser.removeEventListener("pageshow", this, true);
this.browser.removeEventListener("MozApplicationManifest", this, true);
this.browser.removeEventListener("TabPreZombify", this, true);
this.browser.removeEventListener("DOMServiceWorkerFocusClient", this, true);
this.browser.removeEventListener("DOMWindowFocus", this, true);
this.browser.removeEventListener("PluginBindingAttached", this, true, true);
this.browser.removeEventListener("VideoBindingAttached", this, true, true);
@ -4268,7 +4268,7 @@ Tab.prototype = {
break;
}
case "DOMServiceWorkerFocusClient": {
case "DOMWindowFocus": {
GlobalEventDispatcher.sendRequest({
type: "Tab:Select",
tabID: this.id,