Bug 1239349 - Implement webNavigation.onHistoryStateUpdated. r=kmag

MozReview-Commit-ID: FvtkZpcJYCU

--HG--
extra : transplant_source : %0B%CA%07NX7d%17W%D5%A0P%7D%0B%A8yG%E4l%CE
This commit is contained in:
Luca Greco 2016-02-12 02:13:19 +01:00
Родитель 497853f641
Коммит c56fcca8c3
6 изменённых файлов: 123 добавлений и 15 удалений

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

@ -79,6 +79,7 @@ extensions.registerSchemaAPI("webNavigation", "webNavigation", (extension, conte
onCompleted: new WebNavigationEventManager(context, "onCompleted").api(),
onErrorOccurred: new WebNavigationEventManager(context, "onErrorOccurred").api(),
onReferenceFragmentUpdated: new WebNavigationEventManager(context, "onReferenceFragmentUpdated").api(),
onHistoryStateUpdated: new WebNavigationEventManager(context, "onHistoryStateUpdated").api(),
onCreatedNavigationTarget: ignoreEvent(context, "webNavigation.onCreatedNavigationTarget"),
getAllFrames(details) {
let tab = TabManager.getTab(details.tabId);

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

@ -345,7 +345,6 @@
},
{
"name": "onHistoryStateUpdated",
"unsupported": true,
"type": "function",
"description": "Fired when the frame's history was updated to a new URL. All future events for that frame will use the updated URL.",
"filters": [

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

@ -24,6 +24,7 @@ function backgroundScript() {
"onCompleted",
"onErrorOccurred",
"onReferenceFragmentUpdated",
"onHistoryStateUpdated",
];
let expectedTabId = -1;
@ -68,6 +69,7 @@ const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mo
const URL = BASE + "/file_WebNavigation_page1.html";
const FRAME = BASE + "/file_WebNavigation_page2.html";
const FRAME2 = BASE + "/file_WebNavigation_page3.html";
const FRAME_PUSHSTATE = BASE + "/file_WebNavigation_page3_pushState.html";
const REQUIRED = [
"onBeforeNavigate",
@ -155,10 +157,71 @@ add_task(function* webnav_ordering() {
checkRequired(FRAME2);
yield loadAndWait(win, "onReferenceFragmentUpdated", FRAME2 + "#ref",
() => { win.frames[0].document.getElementById("elt").click(); });
let navigationSequence = [
{
action: () => { win.frames[0].document.getElementById("elt").click(); },
waitURL: `${FRAME2}#ref`,
expectedEvent: "onReferenceFragmentUpdated",
description: "clicked an anchor link",
},
{
action: () => { win.frames[0].history.pushState({}, "History PushState", `${FRAME2}#ref2`); },
waitURL: `${FRAME2}#ref2`,
expectedEvent: "onReferenceFragmentUpdated",
description: "history.pushState, same pathname, different hash",
},
{
action: () => { win.frames[0].history.pushState({}, "History PushState", `${FRAME2}#ref2`); },
waitURL: `${FRAME2}#ref2`,
expectedEvent: "onHistoryStateUpdated",
description: "history.pushState, same pathname, same hash",
},
{
action: () => {
win.frames[0].history.pushState({}, "History PushState", `${FRAME2}?query_param1=value#ref2`);
},
waitURL: `${FRAME2}?query_param1=value#ref2`,
expectedEvent: "onHistoryStateUpdated",
description: "history.pushState, same pathname, same hash, different query params",
},
{
action: () => {
win.frames[0].history.pushState({}, "History PushState", `${FRAME2}?query_param2=value#ref3`);
},
waitURL: `${FRAME2}?query_param2=value#ref3`,
expectedEvent: "onHistoryStateUpdated",
description: "history.pushState, same pathname, different hash, different query params",
},
{
action: () => { win.frames[0].history.pushState(null, "History PushState", FRAME_PUSHSTATE); },
waitURL: FRAME_PUSHSTATE,
expectedEvent: "onHistoryStateUpdated",
description: "history.pushState, different pathname",
},
];
info("Received onReferenceFragmentUpdated from FRAME2");
for (let navigation of navigationSequence) {
let {expectedEvent, waitURL, action, description} = navigation;
info(`Waiting ${expectedEvent} from ${waitURL} - ${description}`);
yield loadAndWait(win, expectedEvent, waitURL, action);
info(`Received ${expectedEvent} from ${waitURL} - ${description}`);
}
for (let i = navigationSequence.length - 1; i > 0; i--) {
let {waitURL: fromURL, expectedEvent} = navigationSequence[i];
let {waitURL} = navigationSequence[i - 1];
info(`Waiting ${expectedEvent} from ${waitURL} - history.back() from ${fromURL} to ${waitURL}`);
yield loadAndWait(win, expectedEvent, waitURL, () => { win.frames[0].history.back(); });
info(`Received ${expectedEvent} from ${waitURL} - history.back() from ${fromURL} to ${waitURL}`);
}
for (let i = 0; i < navigationSequence.length - 1; i++) {
let {waitURL: fromURL} = navigationSequence[i];
let {waitURL, expectedEvent} = navigationSequence[i + 1];
info(`Waiting ${expectedEvent} from ${waitURL} - history.forward() from ${fromURL} to ${waitURL}`);
yield loadAndWait(win, expectedEvent, waitURL, () => { win.frames[0].history.forward(); });
info(`Received ${expectedEvent} from ${waitURL} - history.forward() from ${fromURL} to ${waitURL}`);
}
win.close();

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

@ -99,8 +99,11 @@ var Manager = {
onLocationChange(browser, data) {
let url = data.location;
if (data.flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
if (data.isReferenceFragmentUpdated) {
this.fire("onReferenceFragmentUpdated", browser, data, {url});
} else if (data.isHistoryStateUpdated) {
this.fire("onHistoryStateUpdated", browser, data, {url});
} else {
this.fire("onCommitted", browser, data, {url});
}
@ -142,9 +145,8 @@ const EVENTS = [
"onCompleted",
"onErrorOccurred",
"onReferenceFragmentUpdated",
"onHistoryStateUpdated",
// "onCreatedNavigationTarget",
// "onHistoryStateUpdated",
];
var WebNavigation = {};

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

@ -25,6 +25,19 @@ addMessageListener("Extension:DisableWebNavigation", () => {
var WebProgressListener = {
init: function() {
// This WeakMap (DOMWindow -> nsIURI) keeps track of the pathname and hash
// of the previous location for all the existent docShells.
this.previousURIMap = new WeakMap();
// Populate the above previousURIMap by iterating over the docShells tree.
for (let currentDocShell of WebNavigationFrames.iterateDocShellTree(docShell)) {
let win = currentDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
let {currentURI} = currentDocShell.QueryInterface(Ci.nsIWebNavigation);
this.previousURIMap.set(win, currentURI);
}
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
@ -48,6 +61,7 @@ var WebProgressListener = {
status,
stateFlags,
};
sendAsyncMessage("Extension:StateChange", data);
if (webProgress.DOMWindow.top != webProgress.DOMWindow) {
@ -56,24 +70,51 @@ var WebProgressListener = {
// For some reason we don't fire onLocationChange for the
// initial navigation of a sub-frame. So we need to simulate
// it here.
let data = {
location: request.QueryInterface(Ci.nsIChannel).URI.spec,
windowId: webProgress.DOMWindowID,
parentWindowId: WebNavigationFrames.getParentWindowId(webProgress.DOMWindow),
flags: 0,
};
sendAsyncMessage("Extension:LocationChange", data);
this.onLocationChange(webProgress, request, request.QueryInterface(Ci.nsIChannel).URI, 0);
}
}
},
onLocationChange: function onLocationChange(webProgress, request, locationURI, flags) {
let {DOMWindow, loadType} = webProgress;
// Get the previous URI loaded in the DOMWindow.
let previousURI = this.previousURIMap.get(DOMWindow);
// Update the URI in the map with the new locationURI.
this.previousURIMap.set(DOMWindow, locationURI);
let isSameDocument = (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
let isHistoryStateUpdated = false;
let isReferenceFragmentUpdated = false;
if (isSameDocument) {
let pathChanged = !(previousURI && locationURI.equalsExceptRef(previousURI));
let hashChanged = !(previousURI && previousURI.ref == locationURI.ref);
// When the location changes but the document is the same:
// - path not changed and hash changed -> |onReferenceFragmentUpdated|
// (even if it changed using |history.pushState|)
// - path not changed and hash not changed -> |onHistoryStateUpdated|
// (only if it changes using |history.pushState|)
// - path changed -> |onHistoryStateUpdated|
if (!pathChanged && hashChanged) {
isReferenceFragmentUpdated = true;
} else if (loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE) {
isHistoryStateUpdated = true;
} else if (loadType & Ci.nsIDocShell.LOAD_CMD_HISTORY) {
isHistoryStateUpdated = true;
}
}
let data = {
isHistoryStateUpdated, isReferenceFragmentUpdated,
location: locationURI ? locationURI.spec : "",
windowId: webProgress.DOMWindowID,
parentWindowId: WebNavigationFrames.getParentWindowId(webProgress.DOMWindow),
flags,
};
sendAsyncMessage("Extension:LocationChange", data);
},

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

@ -97,6 +97,8 @@ function findFrame(windowId, rootDocShell) {
}
var WebNavigationFrames = {
iterateDocShellTree,
getFrame(docShell, frameId) {
if (frameId == 0) {
return convertDocShellToFrameDetail(docShell);