зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2ginbound, a=merge
This commit is contained in:
Коммит
74267b146f
|
@ -1657,6 +1657,9 @@ pref("browser.newtabpage.directory.source", "https://tiles.services.mozilla.com/
|
|||
// endpoint to send newtab click and view pings
|
||||
pref("browser.newtabpage.directory.ping", "https://tiles.services.mozilla.com/v3/links/");
|
||||
|
||||
// activates the remote-hosted newtab page
|
||||
pref("browser.newtabpage.remote", false);
|
||||
|
||||
// Enable the DOM fullscreen API.
|
||||
pref("full-screen-api.enabled", true);
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "GMPInstallManager",
|
|||
"resource://gre/modules/GMPInstallManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
||||
"resource:///modules/ContentSearch.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
|
||||
|
@ -3511,8 +3513,11 @@ const BrowserSearch = {
|
|||
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
||||
let url = gBrowser.currentURI.spec.toLowerCase();
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let newTabRemoted = Services.prefs.getBoolPref("browser.newtabpage.remote");
|
||||
if (url === "about:home" ||
|
||||
(url === "about:newtab" && NewTabUtils.allPages.enabled)) {
|
||||
(url === "about:newtab" &&
|
||||
((!newTabRemoted && NewTabUtils.allPages.enabled) ||
|
||||
(newTabRemoted && RemoteNewTabUtils.allPages.enabled)))) {
|
||||
ContentSearch.focusInput(mm);
|
||||
} else {
|
||||
openUILinkIn("about:home", "current");
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* 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/. */
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#remotedoc {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
position: absolute;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/* 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/. */
|
||||
/*globals XPCOMUtils, Components, sendAsyncMessage, addMessageListener, removeMessageListener,
|
||||
Services, PrivateBrowsingUtils*/
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu, interfaces: Ci} = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
(function() {
|
||||
let remoteNewTabLocation;
|
||||
let remoteIFrame;
|
||||
|
||||
/**
|
||||
* Attempts to handle commands sent from the remote IFrame within this content frame.
|
||||
* Expected commands below, with data types explained.
|
||||
*
|
||||
* @returns {Boolean} whether or not the command was handled
|
||||
* @param {String} command
|
||||
* The command passed from the remote IFrame
|
||||
* @param {Object} data
|
||||
* Parameters to the command
|
||||
*/
|
||||
function handleCommand(command, data) {
|
||||
let commandHandled = true;
|
||||
switch (command) {
|
||||
case "NewTab:UpdateTelemetryProbe":
|
||||
/**
|
||||
* Update a given Telemetry histogram
|
||||
*
|
||||
* @param {String} data.probe
|
||||
* Probe name to update
|
||||
* @param {Number} data.value
|
||||
* Value to update histogram by
|
||||
*/
|
||||
Services.telemetry.getHistogramById(data.probe).add(data.value);
|
||||
break;
|
||||
case "NewTab:Register":
|
||||
registerEvent(data.type);
|
||||
break;
|
||||
case "NewTab:GetInitialState":
|
||||
getInitialState();
|
||||
break;
|
||||
default:
|
||||
commandHandled = false;
|
||||
}
|
||||
return commandHandled;
|
||||
}
|
||||
|
||||
function initRemotePage(initData) {
|
||||
// Messages that the iframe sends the browser will be passed onto
|
||||
// the privileged parent process
|
||||
remoteNewTabLocation = initData;
|
||||
remoteIFrame = document.querySelector("#remotedoc");
|
||||
|
||||
let loadHandler = () => {
|
||||
if (remoteIFrame.src !== remoteNewTabLocation.href) {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteIFrame.removeEventListener("load", loadHandler);
|
||||
|
||||
remoteIFrame.contentDocument.addEventListener("NewTabCommand", (e) => {
|
||||
// If the commands are not handled within this content frame, the command will be
|
||||
// passed on to main process, in RemoteAboutNewTab.jsm
|
||||
let handled = handleCommand(e.detail.command, e.detail.data);
|
||||
if (!handled) {
|
||||
sendAsyncMessage(e.detail.command, e.detail.data);
|
||||
}
|
||||
});
|
||||
registerEvent("NewTab:Observe");
|
||||
let ev = new CustomEvent("NewTabCommandReady");
|
||||
remoteIFrame.contentDocument.dispatchEvent(ev);
|
||||
};
|
||||
|
||||
remoteIFrame.src = remoteNewTabLocation.href;
|
||||
remoteIFrame.addEventListener("load", loadHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the content IFrame to register a listener to an event sent by
|
||||
* the privileged parent process, in RemoteAboutNewTab.jsm
|
||||
*
|
||||
* @param {String} eventName
|
||||
* Event name to listen to
|
||||
*/
|
||||
function registerEvent(eventName) {
|
||||
addMessageListener(eventName, (message) => {
|
||||
remoteIFrame.contentWindow.postMessage(message, remoteNewTabLocation.origin);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the initial data payload to a content IFrame so it can bootstrap
|
||||
*/
|
||||
function getInitialState() {
|
||||
let prefs = Services.prefs;
|
||||
let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
|
||||
let state = {
|
||||
enabled: prefs.getBoolPref("browser.newtabpage.enabled"),
|
||||
enhanced: prefs.getBoolPref("browser.newtabpage.enhanced"),
|
||||
rows: prefs.getIntPref("browser.newtabpage.rows"),
|
||||
columns: prefs.getIntPref("browser.newtabpage.columns"),
|
||||
introShown: prefs.getBoolPref("browser.newtabpage.introShown"),
|
||||
windowID: window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID,
|
||||
privateBrowsingMode: isPrivate
|
||||
};
|
||||
remoteIFrame.contentWindow.postMessage({
|
||||
name: "NewTab:State",
|
||||
data: state
|
||||
}, remoteNewTabLocation.origin);
|
||||
}
|
||||
|
||||
addMessageListener("NewTabFrame:Init", function loadHandler(message) {
|
||||
// Everything is loaded. Initialize the New Tab Page.
|
||||
removeMessageListener("NewTabFrame:Init", loadHandler);
|
||||
initRemotePage(message.data);
|
||||
});
|
||||
sendAsyncMessage("NewTabFrame:GetInit");
|
||||
}());
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
||||
%newTabDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&newtab.pageTitle;</title>
|
||||
<link rel="stylesheet" href="chrome://browser/content/remote-newtab/newTab.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="remotedoc"/>
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/remote-newtab/newTab.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -109,6 +109,9 @@ browser.jar:
|
|||
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
|
||||
content/browser/newtab/newTab.css (content/newtab/newTab.css)
|
||||
content/browser/newtab/newTab.inadjacent.json (content/newtab/newTab.inadjacent.json)
|
||||
content/browser/remote-newtab/newTab.xhtml (content/remote-newtab/newTab.xhtml)
|
||||
* content/browser/remote-newtab/newTab.js (content/remote-newtab/newTab.js)
|
||||
content/browser/remote-newtab/newTab.css (content/remote-newtab/newTab.css)
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
|
|
|
@ -87,6 +87,9 @@ static RedirEntry kRedirMap[] = {
|
|||
nsIAboutModule::ENABLE_INDEXED_DB },
|
||||
{ "newtab", "chrome://browser/content/newtab/newTab.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "remote-newtab", "chrome://browser/content/remote-newtab/newTab.xhtml",
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
|
||||
|
|
|
@ -107,6 +107,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||
#endif
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "remote-newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
|
||||
|
|
|
@ -13,6 +13,7 @@ DIRS += [
|
|||
'feeds',
|
||||
'loop',
|
||||
'migration',
|
||||
'newtab',
|
||||
'places',
|
||||
'pocket',
|
||||
'preferences',
|
||||
|
|
|
@ -14,10 +14,15 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
|||
|
||||
this.NewTabURL = {
|
||||
_url: "about:newtab",
|
||||
_remoteUrl: "about:remote-newtab",
|
||||
_overridden: false,
|
||||
|
||||
get: function() {
|
||||
return this._url;
|
||||
let output = this._url;
|
||||
if (Services.prefs.getBoolPref("browser.newtabpage.remote")) {
|
||||
output = this._remoteUrl;
|
||||
}
|
||||
return output;
|
||||
},
|
||||
|
||||
get overridden() {
|
|
@ -0,0 +1,271 @@
|
|||
/* 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/. */
|
||||
|
||||
/* global XPCOMUtils, Services, BinarySearch, PlacesUtils, gPrincipal, EventEmitter */
|
||||
/* global gLinks */
|
||||
/* exported PlacesProvider */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PlacesProvider"];
|
||||
|
||||
const {interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BinarySearch",
|
||||
"resource://gre/modules/BinarySearch.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() {
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
return EventEmitter;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPrincipal", function() {
|
||||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
return Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
|
||||
});
|
||||
|
||||
// The maximum number of results PlacesProvider retrieves from history.
|
||||
const HISTORY_RESULTS_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Singleton that checks if a given link should be displayed on about:newtab
|
||||
* or if we should rather not do it for security reasons. URIs that inherit
|
||||
* their caller's principal will be filtered.
|
||||
*/
|
||||
let LinkChecker = {
|
||||
_cache: new Map(),
|
||||
|
||||
get flags() {
|
||||
return Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL |
|
||||
Ci.nsIScriptSecurityManager.DONT_REPORT_ERRORS;
|
||||
},
|
||||
|
||||
checkLoadURI: function LinkChecker_checkLoadURI(aURI) {
|
||||
if (!this._cache.has(aURI)) {
|
||||
this._cache.set(aURI, this._doCheckLoadURI(aURI));
|
||||
}
|
||||
|
||||
return this._cache.get(aURI);
|
||||
},
|
||||
|
||||
_doCheckLoadURI: function LinkChecker_doCheckLoadURI(aURI) {
|
||||
let result = false;
|
||||
try {
|
||||
Services.scriptSecurityManager.
|
||||
checkLoadURIStrWithPrincipal(gPrincipal, aURI, this.flags);
|
||||
result = true;
|
||||
} catch (e) {
|
||||
// We got a weird URI or one that would inherit the caller's principal.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that provides utility functions for links.
|
||||
* A link is a plain object that looks like this:
|
||||
*
|
||||
* {
|
||||
* url: "http://www.mozilla.org/",
|
||||
* title: "Mozilla",
|
||||
* frecency: 1337,
|
||||
* lastVisitDate: 1394678824766431,
|
||||
* }
|
||||
*/
|
||||
const LinkUtils = {
|
||||
_sortProperties: [
|
||||
"frecency",
|
||||
"lastVisitDate",
|
||||
"url",
|
||||
],
|
||||
|
||||
/**
|
||||
* Compares two links.
|
||||
*
|
||||
* @param {String} aLink1 The first link.
|
||||
* @param {String} aLink2 The second link.
|
||||
* @return {Number} A negative number if aLink1 is ordered before aLink2, zero if
|
||||
* aLink1 and aLink2 have the same ordering, or a positive number if
|
||||
* aLink1 is ordered after aLink2.
|
||||
* Order is ascending.
|
||||
*/
|
||||
compareLinks: function LinkUtils_compareLinks(aLink1, aLink2) {
|
||||
for (let prop of LinkUtils._sortProperties) {
|
||||
if (!aLink1.hasOwnProperty(prop) || !aLink2.hasOwnProperty(prop)) {
|
||||
throw new Error("Comparable link missing required property: " + prop);
|
||||
}
|
||||
}
|
||||
return aLink2.frecency - aLink1.frecency ||
|
||||
aLink2.lastVisitDate - aLink1.lastVisitDate ||
|
||||
aLink1.url.localeCompare(aLink2.url);
|
||||
},
|
||||
};
|
||||
|
||||
/* Queries history to retrieve the most visited sites. Emits events when the
|
||||
* history changes.
|
||||
* Implements the EventEmitter interface.
|
||||
*/
|
||||
let Links = function Links() {
|
||||
EventEmitter.decorate(this);
|
||||
};
|
||||
|
||||
Links.prototype = {
|
||||
/**
|
||||
* Set this to change the maximum number of links the provider will provide.
|
||||
*/
|
||||
get maxNumLinks() {
|
||||
// getter, so it can't be replaced dynamically
|
||||
return HISTORY_RESULTS_LIMIT;
|
||||
},
|
||||
|
||||
/**
|
||||
* A set of functions called by @mozilla.org/browser/nav-historyservice
|
||||
* All history events are emitted from this object.
|
||||
*/
|
||||
historyObserver: {
|
||||
onDeleteURI: function historyObserver_onDeleteURI(aURI) {
|
||||
// let observers remove sensetive data associated with deleted visit
|
||||
gLinks.emit("deleteURI", {
|
||||
url: aURI.spec,
|
||||
});
|
||||
},
|
||||
|
||||
onClearHistory: function historyObserver_onClearHistory() {
|
||||
gLinks.emit("clearHistory");
|
||||
},
|
||||
|
||||
onFrecencyChanged: function historyObserver_onFrecencyChanged(aURI,
|
||||
aNewFrecency, aGUID, aHidden, aLastVisitDate) { // jshint ignore:line
|
||||
// The implementation of the query in getLinks excludes hidden and
|
||||
// unvisited pages, so it's important to exclude them here, too.
|
||||
if (!aHidden && aLastVisitDate) {
|
||||
gLinks.emit("linkChanged", {
|
||||
url: aURI.spec,
|
||||
frecency: aNewFrecency,
|
||||
lastVisitDate: aLastVisitDate,
|
||||
type: "history",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onManyFrecenciesChanged: function historyObserver_onManyFrecenciesChanged() {
|
||||
// Called when frecencies are invalidated and also when clearHistory is called
|
||||
// See toolkit/components/places/tests/unit/test_frecency_observers.js
|
||||
gLinks.emit("manyLinksChanged");
|
||||
},
|
||||
|
||||
onTitleChanged: function historyObserver_onTitleChanged(aURI, aNewTitle) {
|
||||
gLinks.emit("linkChanged", {
|
||||
url: aURI.spec,
|
||||
title: aNewTitle
|
||||
});
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
},
|
||||
|
||||
/**
|
||||
* Must be called before the provider is used.
|
||||
* Makes it easy to disable under pref
|
||||
*/
|
||||
init: function PlacesProvider_init() {
|
||||
PlacesUtils.history.addObserver(this.historyObserver, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Must be called before the provider is unloaded.
|
||||
*/
|
||||
destroy: function PlacesProvider_destroy() {
|
||||
PlacesUtils.history.removeObserver(this.historyObserver);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current set of links delivered by this provider.
|
||||
*
|
||||
* @returns {Promise} Returns a promise with the array of links as payload.
|
||||
*/
|
||||
getLinks: function PlacesProvider_getLinks() {
|
||||
let getLinksPromise = new Promise((resolve, reject) => {
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.maxResults = this.maxNumLinks;
|
||||
|
||||
// Sort by frecency, descending.
|
||||
options.sortingMode = Ci.nsINavHistoryQueryOptions
|
||||
.SORT_BY_FRECENCY_DESCENDING;
|
||||
|
||||
let links = [];
|
||||
|
||||
let queryHandlers = {
|
||||
handleResult: function(aResultSet) {
|
||||
for (let row = aResultSet.getNextRow(); row; row = aResultSet.getNextRow()) {
|
||||
let url = row.getResultByIndex(1);
|
||||
if (LinkChecker.checkLoadURI(url)) {
|
||||
let link = {
|
||||
url: url,
|
||||
title: row.getResultByIndex(2),
|
||||
frecency: row.getResultByIndex(12),
|
||||
lastVisitDate: row.getResultByIndex(5),
|
||||
type: "history",
|
||||
};
|
||||
links.push(link);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleError: function(aError) {
|
||||
reject(aError);
|
||||
},
|
||||
|
||||
handleCompletion: function(aReason) { // jshint ignore:line
|
||||
// The Places query breaks ties in frecency by place ID descending, but
|
||||
// that's different from how Links.compareLinks breaks ties, because
|
||||
// compareLinks doesn't have access to place IDs. It's very important
|
||||
// that the initial list of links is sorted in the same order imposed by
|
||||
// compareLinks, because Links uses compareLinks to perform binary
|
||||
// searches on the list. So, ensure the list is so ordered.
|
||||
let i = 1;
|
||||
let outOfOrder = [];
|
||||
while (i < links.length) {
|
||||
if (LinkUtils.compareLinks(links[i - 1], links[i]) > 0) {
|
||||
outOfOrder.push(links.splice(i, 1)[0]);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
for (let link of outOfOrder) {
|
||||
i = BinarySearch.insertionIndexOf(LinkUtils.compareLinks, links, link);
|
||||
links.splice(i, 0, link);
|
||||
}
|
||||
|
||||
resolve(links);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the query.
|
||||
let query = PlacesUtils.history.getNewQuery();
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase);
|
||||
db.asyncExecuteLegacyQueries([query], 1, options, queryHandlers);
|
||||
});
|
||||
|
||||
return getLinksPromise;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that serves as the default link provider for the grid.
|
||||
*/
|
||||
const gLinks = new Links(); // jshint ignore:line
|
||||
|
||||
let PlacesProvider = {
|
||||
LinkChecker: LinkChecker,
|
||||
LinkUtils: LinkUtils,
|
||||
links: gLinks,
|
||||
};
|
|
@ -0,0 +1,233 @@
|
|||
/* 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/. */
|
||||
|
||||
/* globals Services, XPCOMUtils, RemotePages, RemoteNewTabLocation, RemoteNewTabUtils, Task */
|
||||
/* globals BackgroundPageThumbs, PageThumbs, RemoteDirectoryLinksProvider */
|
||||
/* exported RemoteAboutNewTab */
|
||||
|
||||
"use strict";
|
||||
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteAboutNewTab"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
|
||||
"resource://gre/modules/RemotePageManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BackgroundPageThumbs",
|
||||
"resource://gre/modules/BackgroundPageThumbs.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteDirectoryLinksProvider",
|
||||
"resource:///modules/RemoteDirectoryLinksProvider.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabLocation",
|
||||
"resource:///modules/RemoteNewTabLocation.jsm");
|
||||
|
||||
let RemoteAboutNewTab = {
|
||||
|
||||
pageListener: null,
|
||||
|
||||
/**
|
||||
* Initialize the RemotePageManager and add all message listeners for this page
|
||||
*/
|
||||
init: function() {
|
||||
this.pageListener = new RemotePages("about:remote-newtab");
|
||||
this.pageListener.addMessageListener("NewTab:InitializeGrid", this.initializeGrid.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:UpdateGrid", this.updateGrid.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:CaptureBackgroundPageThumbs",
|
||||
this.captureBackgroundPageThumb.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:PageThumbs", this.createPageThumb.bind(this));
|
||||
this.pageListener.addMessageListener("NewTabFrame:GetInit", this.initContentFrame.bind(this));
|
||||
|
||||
this._addObservers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the grid for the first time when the page loads.
|
||||
* Fetch all the links and send them down to the child to populate
|
||||
* the grid with.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message.
|
||||
*/
|
||||
initializeGrid: function(message) {
|
||||
RemoteNewTabUtils.links.populateCache(() => {
|
||||
message.target.sendAsyncMessage("NewTab:InitializeLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Inits the content iframe with the newtab location
|
||||
*/
|
||||
initContentFrame: function(message) {
|
||||
message.target.sendAsyncMessage("NewTabFrame:Init", {
|
||||
href: RemoteNewTabLocation.href,
|
||||
origin: RemoteNewTabLocation.origin
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the grid by getting a new set of links.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message.
|
||||
*/
|
||||
updateGrid: function(message) {
|
||||
message.target.sendAsyncMessage("NewTab:UpdateLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Captures the site's thumbnail in the background, then attemps to show the thumbnail.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message with the following data:
|
||||
*
|
||||
* link (Object):
|
||||
* A link object that contains:
|
||||
*
|
||||
* baseDomain (String)
|
||||
* blockState (Boolean)
|
||||
* frecency (Integer)
|
||||
* lastVisiteDate (Integer)
|
||||
* pinState (Boolean)
|
||||
* title (String)
|
||||
* type (String)
|
||||
* url (String)
|
||||
*/
|
||||
captureBackgroundPageThumb: Task.async(function* (message) {
|
||||
try {
|
||||
yield BackgroundPageThumbs.captureIfMissing(message.data.link.url);
|
||||
this.createPageThumb(message);
|
||||
} catch (err) {
|
||||
Cu.reportError("error: " + err);
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates the thumbnail to display for each site based on the unique URL
|
||||
* of the site and it's type (regular or enhanced). If the thumbnail is of
|
||||
* type "regular", we create a blob and send that down to the child. If the
|
||||
* thumbnail is of type "enhanced", get the file path for the URL and create
|
||||
* and enhanced URI that will be sent down to the child.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message with the following data:
|
||||
*
|
||||
* link (Object):
|
||||
* A link object that contains:
|
||||
*
|
||||
* baseDomain (String)
|
||||
* blockState (Boolean)
|
||||
* frecency (Integer)
|
||||
* lastVisiteDate (Integer)
|
||||
* pinState (Boolean)
|
||||
* title (String)
|
||||
* type (String)
|
||||
* url (String)
|
||||
*/
|
||||
createPageThumb: function(message) {
|
||||
let imgSrc = PageThumbs.getThumbnailURL(message.data.link.url);
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
let img = doc.createElementNS(XHTML_NAMESPACE, "img");
|
||||
let canvas = doc.createElementNS(XHTML_NAMESPACE, "canvas");
|
||||
let enhanced = Services.prefs.getBoolPref("browser.newtabpage.enhanced");
|
||||
|
||||
img.onload = function(e) { // jshint ignore:line
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(this, 0, 0, this.naturalWidth, this.naturalHeight);
|
||||
canvas.toBlob(function(blob) {
|
||||
let host = new URL(message.data.link.url).host;
|
||||
RemoteAboutNewTab.pageListener.sendAsyncMessage("NewTab:RegularThumbnailURI", {
|
||||
thumbPath: "/pagethumbs/" + host,
|
||||
enhanced,
|
||||
url: message.data.link.url,
|
||||
blob,
|
||||
});
|
||||
});
|
||||
};
|
||||
img.src = imgSrc;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the set of enhanced links (if any) from the Directory Links Provider.
|
||||
*/
|
||||
getEnhancedLinks: function() {
|
||||
let enhancedLinks = [];
|
||||
for (let link of RemoteNewTabUtils.links.getLinks()) {
|
||||
if (link) {
|
||||
enhancedLinks.push(RemoteDirectoryLinksProvider.getEnhancedLink(link));
|
||||
}
|
||||
}
|
||||
return enhancedLinks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for a preference change or session purge for all pages and sends
|
||||
* a message to update the pages that are open. If a session purge occured,
|
||||
* also clear the links cache and update the set of links to display, as they
|
||||
* may have changed, then proceed with the page update.
|
||||
*/
|
||||
observe: function(aSubject, aTopic, aData) { // jshint ignore:line
|
||||
let extraData;
|
||||
let refreshPage = false;
|
||||
if (aTopic === "browser:purge-session-history") {
|
||||
RemoteNewTabUtils.links.resetCache();
|
||||
RemoteNewTabUtils.links.populateCache(() => {
|
||||
this.pageListener.sendAsyncMessage("NewTab:UpdateLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (extraData !== undefined || aTopic === "page-thumbnail:create") {
|
||||
if (aTopic !== "page-thumbnail:create") {
|
||||
// Change the topic for enhanced and enabled observers.
|
||||
aTopic = aData;
|
||||
}
|
||||
this.pageListener.sendAsyncMessage("NewTab:Observe", {topic: aTopic, data: extraData});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add all observers that about:newtab page must listen for.
|
||||
*/
|
||||
_addObservers: function() {
|
||||
Services.obs.addObserver(this, "page-thumbnail:create", true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all observers on the page.
|
||||
*/
|
||||
_removeObservers: function() {
|
||||
Services.obs.removeObserver(this, "page-thumbnail:create");
|
||||
Services.obs.removeObserver(this, "browser:purge-session-history");
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
uninit: function() {
|
||||
this._removeObservers();
|
||||
this.pageListener.destroy();
|
||||
this.pageListener = null;
|
||||
},
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,42 @@
|
|||
/* globals Services */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteNewTabLocation"];
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.importGlobalProperties(["URL"]);
|
||||
|
||||
// TODO: will get dynamically set in bug 1210478
|
||||
const DEFAULT_PAGE_LOCATION = "https://newtab.cdn.mozilla.net/v0/nightly/en-US/index.html";
|
||||
|
||||
this.RemoteNewTabLocation = {
|
||||
_url: new URL(DEFAULT_PAGE_LOCATION),
|
||||
_overridden: false,
|
||||
|
||||
get href() {
|
||||
return this._url.href;
|
||||
},
|
||||
|
||||
get origin() {
|
||||
return this._url.origin;
|
||||
},
|
||||
|
||||
get overridden() {
|
||||
return this._overridden;
|
||||
},
|
||||
|
||||
override: function(newURL) {
|
||||
this._url = new URL(newURL);
|
||||
this._overridden = true;
|
||||
Services.obs.notifyObservers(null, "remote-new-tab-location-changed",
|
||||
this._url.href);
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._url = new URL(DEFAULT_PAGE_LOCATION);
|
||||
this._overridden = false;
|
||||
Services.obs.notifyObservers(null, "remote-new-tab-location-changed",
|
||||
this._url.href);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,766 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteNewTabUtils"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BinarySearch",
|
||||
"resource://gre/modules/BinarySearch.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPrincipal", function () {
|
||||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
return Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
});
|
||||
|
||||
// The maximum number of results PlacesProvider retrieves from history.
|
||||
const HISTORY_RESULTS_LIMIT = 100;
|
||||
|
||||
// The maximum number of links Links.getLinks will return.
|
||||
const LINKS_GET_LINKS_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Singleton that serves as the default link provider for the grid. It queries
|
||||
* the history to retrieve the most frequently visited sites.
|
||||
*/
|
||||
let PlacesProvider = {
|
||||
/**
|
||||
* A count of how many batch updates are under way (batches may be nested, so
|
||||
* we keep a counter instead of a simple bool).
|
||||
**/
|
||||
_batchProcessingDepth: 0,
|
||||
|
||||
/**
|
||||
* A flag that tracks whether onFrecencyChanged was notified while a batch
|
||||
* operation was in progress, to tell us whether to take special action after
|
||||
* the batch operation completes.
|
||||
**/
|
||||
_batchCalledFrecencyChanged: false,
|
||||
|
||||
/**
|
||||
* Set this to change the maximum number of links the provider will provide.
|
||||
*/
|
||||
maxNumLinks: HISTORY_RESULTS_LIMIT,
|
||||
|
||||
/**
|
||||
* Must be called before the provider is used.
|
||||
*/
|
||||
init: function PlacesProvider_init() {
|
||||
PlacesUtils.history.addObserver(this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current set of links delivered by this provider.
|
||||
* @param aCallback The function that the array of links is passed to.
|
||||
*/
|
||||
getLinks: function PlacesProvider_getLinks(aCallback) {
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.maxResults = this.maxNumLinks;
|
||||
|
||||
// Sort by frecency, descending.
|
||||
options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_DESCENDING
|
||||
|
||||
let links = [];
|
||||
|
||||
let callback = {
|
||||
handleResult: function (aResultSet) {
|
||||
let row;
|
||||
|
||||
while ((row = aResultSet.getNextRow())) {
|
||||
let url = row.getResultByIndex(1);
|
||||
if (LinkChecker.checkLoadURI(url)) {
|
||||
let title = row.getResultByIndex(2);
|
||||
let frecency = row.getResultByIndex(12);
|
||||
let lastVisitDate = row.getResultByIndex(5);
|
||||
links.push({
|
||||
url: url,
|
||||
title: title,
|
||||
frecency: frecency,
|
||||
lastVisitDate: lastVisitDate,
|
||||
type: "history",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleError: function (aError) {
|
||||
// Should we somehow handle this error?
|
||||
aCallback([]);
|
||||
},
|
||||
|
||||
handleCompletion: function (aReason) {
|
||||
// The Places query breaks ties in frecency by place ID descending, but
|
||||
// that's different from how Links.compareLinks breaks ties, because
|
||||
// compareLinks doesn't have access to place IDs. It's very important
|
||||
// that the initial list of links is sorted in the same order imposed by
|
||||
// compareLinks, because Links uses compareLinks to perform binary
|
||||
// searches on the list. So, ensure the list is so ordered.
|
||||
let i = 1;
|
||||
let outOfOrder = [];
|
||||
while (i < links.length) {
|
||||
if (Links.compareLinks(links[i - 1], links[i]) > 0)
|
||||
outOfOrder.push(links.splice(i, 1)[0]);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
for (let link of outOfOrder) {
|
||||
i = BinarySearch.insertionIndexOf(Links.compareLinks, links, link);
|
||||
links.splice(i, 0, link);
|
||||
}
|
||||
|
||||
aCallback(links);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the query.
|
||||
let query = PlacesUtils.history.getNewQuery();
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase);
|
||||
db.asyncExecuteLegacyQueries([query], 1, options, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers an object that will be notified when the provider's links change.
|
||||
* @param aObserver An object with the following optional properties:
|
||||
* * onLinkChanged: A function that's called when a single link
|
||||
* changes. It's passed the provider and the link object. Only the
|
||||
* link's `url` property is guaranteed to be present. If its `title`
|
||||
* property is present, then its title has changed, and the
|
||||
* property's value is the new title. If any sort properties are
|
||||
* present, then its position within the provider's list of links may
|
||||
* have changed, and the properties' values are the new sort-related
|
||||
* values. Note that this link may not necessarily have been present
|
||||
* in the lists returned from any previous calls to getLinks.
|
||||
* * onManyLinksChanged: A function that's called when many links
|
||||
* change at once. It's passed the provider. You should call
|
||||
* getLinks to get the provider's new list of links.
|
||||
*/
|
||||
addObserver: function PlacesProvider_addObserver(aObserver) {
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onBeginUpdateBatch: function() {
|
||||
this._batchProcessingDepth += 1;
|
||||
},
|
||||
|
||||
onEndUpdateBatch: function() {
|
||||
this._batchProcessingDepth -= 1;
|
||||
if (this._batchProcessingDepth == 0 && this._batchCalledFrecencyChanged) {
|
||||
this.onManyFrecenciesChanged();
|
||||
this._batchCalledFrecencyChanged = false;
|
||||
}
|
||||
},
|
||||
|
||||
onDeleteURI: function PlacesProvider_onDeleteURI(aURI, aGUID, aReason) {
|
||||
// let observers remove sensetive data associated with deleted visit
|
||||
this._callObservers("onDeleteURI", {
|
||||
url: aURI.spec,
|
||||
});
|
||||
},
|
||||
|
||||
onClearHistory: function() {
|
||||
this._callObservers("onClearHistory")
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onFrecencyChanged: function PlacesProvider_onFrecencyChanged(aURI, aNewFrecency, aGUID, aHidden, aLastVisitDate) {
|
||||
// If something is doing a batch update of history entries we don't want
|
||||
// to do lots of work for each record. So we just track the fact we need
|
||||
// to call onManyFrecenciesChanged() once the batch is complete.
|
||||
if (this._batchProcessingDepth > 0) {
|
||||
this._batchCalledFrecencyChanged = true;
|
||||
return;
|
||||
}
|
||||
// The implementation of the query in getLinks excludes hidden and
|
||||
// unvisited pages, so it's important to exclude them here, too.
|
||||
if (!aHidden && aLastVisitDate) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: aURI.spec,
|
||||
frecency: aNewFrecency,
|
||||
lastVisitDate: aLastVisitDate,
|
||||
type: "history",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onManyFrecenciesChanged: function PlacesProvider_onManyFrecenciesChanged() {
|
||||
this._callObservers("onManyLinksChanged");
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onTitleChanged: function PlacesProvider_onTitleChanged(aURI, aNewTitle, aGUID) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: aURI.spec,
|
||||
title: aNewTitle
|
||||
});
|
||||
},
|
||||
|
||||
_callObservers: function PlacesProvider__callObservers(aMethodName, aArg) {
|
||||
for (let obs of this._observers) {
|
||||
if (obs[aMethodName]) {
|
||||
try {
|
||||
obs[aMethodName](this, aArg);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that provides access to all links contained in the grid (including
|
||||
* the ones that don't fit on the grid). A link is a plain object that looks
|
||||
* like this:
|
||||
*
|
||||
* {
|
||||
* url: "http://www.mozilla.org/",
|
||||
* title: "Mozilla",
|
||||
* frecency: 1337,
|
||||
* lastVisitDate: 1394678824766431,
|
||||
* }
|
||||
*/
|
||||
let Links = {
|
||||
/**
|
||||
* The maximum number of links returned by getLinks.
|
||||
*/
|
||||
maxNumLinks: LINKS_GET_LINKS_LIMIT,
|
||||
|
||||
/**
|
||||
* A mapping from each provider to an object { sortedLinks, siteMap, linkMap }.
|
||||
* sortedLinks is the cached, sorted array of links for the provider.
|
||||
* siteMap is a mapping from base domains to URL count associated with the domain.
|
||||
* siteMap is used to look up a user's top sites that can be targeted
|
||||
* with a suggested tile.
|
||||
* linkMap is a Map from link URLs to link objects.
|
||||
*/
|
||||
_providers: new Map(),
|
||||
|
||||
/**
|
||||
* The properties of link objects used to sort them.
|
||||
*/
|
||||
_sortProperties: [
|
||||
"frecency",
|
||||
"lastVisitDate",
|
||||
"url",
|
||||
],
|
||||
|
||||
/**
|
||||
* List of callbacks waiting for the cache to be populated.
|
||||
*/
|
||||
_populateCallbacks: [],
|
||||
|
||||
/**
|
||||
* A list of objects that are observing links updates.
|
||||
*/
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* Registers an object that will be notified when links updates.
|
||||
*/
|
||||
addObserver: function (aObserver) {
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a link provider.
|
||||
* @param aProvider The link provider.
|
||||
*/
|
||||
addProvider: function Links_addProvider(aProvider) {
|
||||
this._providers.set(aProvider, null);
|
||||
aProvider.addObserver(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a link provider.
|
||||
* @param aProvider The link provider.
|
||||
*/
|
||||
removeProvider: function Links_removeProvider(aProvider) {
|
||||
if (!this._providers.delete(aProvider))
|
||||
throw new Error("Unknown provider");
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates the cache with fresh links from the providers.
|
||||
* @param aCallback The callback to call when finished (optional).
|
||||
* @param aForce When true, populates the cache even when it's already filled.
|
||||
*/
|
||||
populateCache: function Links_populateCache(aCallback, aForce) {
|
||||
let callbacks = this._populateCallbacks;
|
||||
|
||||
// Enqueue the current callback.
|
||||
callbacks.push(aCallback);
|
||||
|
||||
// There was a callback waiting already, thus the cache has not yet been
|
||||
// populated.
|
||||
if (callbacks.length > 1)
|
||||
return;
|
||||
|
||||
function executeCallbacks() {
|
||||
while (callbacks.length) {
|
||||
let callback = callbacks.shift();
|
||||
if (callback) {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
// We want to proceed even if a callback fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let numProvidersRemaining = this._providers.size;
|
||||
for (let [provider, links] of this._providers) {
|
||||
this._populateProviderCache(provider, () => {
|
||||
if (--numProvidersRemaining == 0)
|
||||
executeCallbacks();
|
||||
}, aForce);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current set of links contained in the grid.
|
||||
* @return The links in the grid.
|
||||
*/
|
||||
getLinks: function Links_getLinks() {
|
||||
let links = this._getMergedProviderLinks();
|
||||
|
||||
let sites = new Set();
|
||||
|
||||
// Filter duplicate base domains.
|
||||
links = links.filter(function (link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
link.baseDomain = site;
|
||||
if (site == null || sites.has(site))
|
||||
return false;
|
||||
sites.add(site);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return links;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the links cache.
|
||||
*/
|
||||
resetCache: function Links_resetCache() {
|
||||
for (let provider of this._providers.keys()) {
|
||||
this._providers.set(provider, null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Compares two links.
|
||||
* @param aLink1 The first link.
|
||||
* @param aLink2 The second link.
|
||||
* @return A negative number if aLink1 is ordered before aLink2, zero if
|
||||
* aLink1 and aLink2 have the same ordering, or a positive number if
|
||||
* aLink1 is ordered after aLink2.
|
||||
*
|
||||
* @note compareLinks's this object is bound to Links below.
|
||||
*/
|
||||
compareLinks: function Links_compareLinks(aLink1, aLink2) {
|
||||
for (let prop of this._sortProperties) {
|
||||
if (!(prop in aLink1) || !(prop in aLink2))
|
||||
throw new Error("Comparable link missing required property: " + prop);
|
||||
}
|
||||
return aLink2.frecency - aLink1.frecency ||
|
||||
aLink2.lastVisitDate - aLink1.lastVisitDate ||
|
||||
aLink1.url.localeCompare(aLink2.url);
|
||||
},
|
||||
|
||||
_incrementSiteMap: function(map, link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
map.set(site, (map.get(site) || 0) + 1);
|
||||
},
|
||||
|
||||
_decrementSiteMap: function(map, link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
let previousURLCount = map.get(site);
|
||||
if (previousURLCount === 1) {
|
||||
map.delete(site);
|
||||
} else {
|
||||
map.set(site, previousURLCount - 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the siteMap cache based on the link given and whether we need
|
||||
* to increment or decrement it. We do this by iterating over all stored providers
|
||||
* to find which provider this link already exists in. For providers that
|
||||
* have this link, we will adjust siteMap for them accordingly.
|
||||
*
|
||||
* @param aLink The link that will affect siteMap
|
||||
* @param increment A boolean for whether to increment or decrement siteMap
|
||||
*/
|
||||
_adjustSiteMapAndNotify: function(aLink, increment=true) {
|
||||
for (let [provider, cache] of this._providers) {
|
||||
// We only update siteMap if aLink is already stored in linkMap.
|
||||
if (cache.linkMap.get(aLink.url)) {
|
||||
if (increment) {
|
||||
this._incrementSiteMap(cache.siteMap, aLink);
|
||||
continue;
|
||||
}
|
||||
this._decrementSiteMap(cache.siteMap, aLink);
|
||||
}
|
||||
}
|
||||
this._callObservers("onLinkChanged", aLink);
|
||||
},
|
||||
|
||||
populateProviderCache: function(provider, callback) {
|
||||
if (!this._providers.has(provider)) {
|
||||
throw new Error("Can only populate provider cache for existing provider.");
|
||||
}
|
||||
|
||||
return this._populateProviderCache(provider, callback, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls getLinks on the given provider and populates our cache for it.
|
||||
* @param aProvider The provider whose cache will be populated.
|
||||
* @param aCallback The callback to call when finished.
|
||||
* @param aForce When true, populates the provider's cache even when it's
|
||||
* already filled.
|
||||
*/
|
||||
_populateProviderCache: function (aProvider, aCallback, aForce) {
|
||||
let cache = this._providers.get(aProvider);
|
||||
let createCache = !cache;
|
||||
if (createCache) {
|
||||
cache = {
|
||||
// Start with a resolved promise.
|
||||
populatePromise: new Promise(resolve => resolve()),
|
||||
};
|
||||
this._providers.set(aProvider, cache);
|
||||
}
|
||||
// Chain the populatePromise so that calls are effectively queued.
|
||||
cache.populatePromise = cache.populatePromise.then(() => {
|
||||
return new Promise(resolve => {
|
||||
if (!createCache && !aForce) {
|
||||
aCallback();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
aProvider.getLinks(links => {
|
||||
// Filter out null and undefined links so we don't have to deal with
|
||||
// them in getLinks when merging links from providers.
|
||||
links = links.filter((link) => !!link);
|
||||
cache.sortedLinks = links;
|
||||
cache.siteMap = links.reduce((map, link) => {
|
||||
this._incrementSiteMap(map, link);
|
||||
return map;
|
||||
}, new Map());
|
||||
cache.linkMap = links.reduce((map, link) => {
|
||||
map.set(link.url, link);
|
||||
return map;
|
||||
}, new Map());
|
||||
aCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges the cached lists of links from all providers whose lists are cached.
|
||||
* @return The merged list.
|
||||
*/
|
||||
_getMergedProviderLinks: function Links__getMergedProviderLinks() {
|
||||
// Build a list containing a copy of each provider's sortedLinks list.
|
||||
let linkLists = [];
|
||||
for (let provider of this._providers.keys()) {
|
||||
let links = this._providers.get(provider);
|
||||
if (links && links.sortedLinks) {
|
||||
linkLists.push(links.sortedLinks.slice());
|
||||
}
|
||||
}
|
||||
|
||||
function getNextLink() {
|
||||
let minLinks = null;
|
||||
for (let links of linkLists) {
|
||||
if (links.length &&
|
||||
(!minLinks || Links.compareLinks(links[0], minLinks[0]) < 0))
|
||||
minLinks = links;
|
||||
}
|
||||
return minLinks ? minLinks.shift() : null;
|
||||
}
|
||||
|
||||
let finalLinks = [];
|
||||
for (let nextLink = getNextLink();
|
||||
nextLink && finalLinks.length < this.maxNumLinks;
|
||||
nextLink = getNextLink()) {
|
||||
finalLinks.push(nextLink);
|
||||
}
|
||||
|
||||
return finalLinks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by a provider to notify us when a single link changes.
|
||||
* @param aProvider The provider whose link changed.
|
||||
* @param aLink The link that changed. If the link is new, it must have all
|
||||
* of the _sortProperties. Otherwise, it may have as few or as
|
||||
* many as is convenient.
|
||||
* @param aIndex The current index of the changed link in the sortedLinks
|
||||
cache in _providers. Defaults to -1 if the provider doesn't know the index
|
||||
* @param aDeleted Boolean indicating if the provider has deleted the link.
|
||||
*/
|
||||
onLinkChanged: function Links_onLinkChanged(aProvider, aLink, aIndex=-1, aDeleted=false) {
|
||||
if (!("url" in aLink))
|
||||
throw new Error("Changed links must have a url property");
|
||||
|
||||
let links = this._providers.get(aProvider);
|
||||
if (!links)
|
||||
// This is not an error, it just means that between the time the provider
|
||||
// was added and the future time we call getLinks on it, it notified us of
|
||||
// a change.
|
||||
return;
|
||||
|
||||
let { sortedLinks, siteMap, linkMap } = links;
|
||||
let existingLink = linkMap.get(aLink.url);
|
||||
let insertionLink = null;
|
||||
|
||||
if (existingLink) {
|
||||
// Update our copy's position in O(lg n) by first removing it from its
|
||||
// list. It's important to do this before modifying its properties.
|
||||
if (this._sortProperties.some(prop => prop in aLink)) {
|
||||
let idx = aIndex;
|
||||
if (idx < 0) {
|
||||
idx = this._indexOf(sortedLinks, existingLink);
|
||||
} else if (this.compareLinks(aLink, sortedLinks[idx]) != 0) {
|
||||
throw new Error("aLink should be the same as sortedLinks[idx]");
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
throw new Error("Link should be in _sortedLinks if in _linkMap");
|
||||
}
|
||||
sortedLinks.splice(idx, 1);
|
||||
|
||||
if (aDeleted) {
|
||||
linkMap.delete(existingLink.url);
|
||||
this._decrementSiteMap(siteMap, existingLink);
|
||||
} else {
|
||||
// Update our copy's properties.
|
||||
Object.assign(existingLink, aLink);
|
||||
|
||||
// Finally, reinsert our copy below.
|
||||
insertionLink = existingLink;
|
||||
}
|
||||
}
|
||||
// Update our copy's title in O(1).
|
||||
if ("title" in aLink && aLink.title != existingLink.title) {
|
||||
existingLink.title = aLink.title;
|
||||
}
|
||||
}
|
||||
else if (this._sortProperties.every(prop => prop in aLink)) {
|
||||
// Before doing the O(lg n) insertion below, do an O(1) check for the
|
||||
// common case where the new link is too low-ranked to be in the list.
|
||||
if (sortedLinks.length && sortedLinks.length == aProvider.maxNumLinks) {
|
||||
let lastLink = sortedLinks[sortedLinks.length - 1];
|
||||
if (this.compareLinks(lastLink, aLink) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Copy the link object so that changes later made to it by the caller
|
||||
// don't affect our copy.
|
||||
insertionLink = {};
|
||||
for (let prop in aLink) {
|
||||
insertionLink[prop] = aLink[prop];
|
||||
}
|
||||
linkMap.set(aLink.url, insertionLink);
|
||||
this._incrementSiteMap(siteMap, aLink);
|
||||
}
|
||||
|
||||
if (insertionLink) {
|
||||
let idx = this._insertionIndexOf(sortedLinks, insertionLink);
|
||||
sortedLinks.splice(idx, 0, insertionLink);
|
||||
if (sortedLinks.length > aProvider.maxNumLinks) {
|
||||
let lastLink = sortedLinks.pop();
|
||||
linkMap.delete(lastLink.url);
|
||||
this._decrementSiteMap(siteMap, lastLink);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by a provider to notify us when many links change.
|
||||
*/
|
||||
onManyLinksChanged: function Links_onManyLinksChanged(aProvider) {
|
||||
this._populateProviderCache(aProvider, () => {}, true);
|
||||
},
|
||||
|
||||
_indexOf: function Links__indexOf(aArray, aLink) {
|
||||
return this._binsearch(aArray, aLink, "indexOf");
|
||||
},
|
||||
|
||||
_insertionIndexOf: function Links__insertionIndexOf(aArray, aLink) {
|
||||
return this._binsearch(aArray, aLink, "insertionIndexOf");
|
||||
},
|
||||
|
||||
_binsearch: function Links__binsearch(aArray, aLink, aMethod) {
|
||||
return BinarySearch[aMethod](this.compareLinks, aArray, aLink);
|
||||
},
|
||||
|
||||
_callObservers(methodName, ...args) {
|
||||
for (let obs of this._observers) {
|
||||
if (typeof(obs[methodName]) == "function") {
|
||||
try {
|
||||
obs[methodName](this, ...args);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Links.compareLinks = Links.compareLinks.bind(Links);
|
||||
|
||||
/**
|
||||
* Singleton that checks if a given link should be displayed on about:newtab
|
||||
* or if we should rather not do it for security reasons. URIs that inherit
|
||||
* their caller's principal will be filtered.
|
||||
*/
|
||||
let LinkChecker = {
|
||||
_cache: {},
|
||||
|
||||
get flags() {
|
||||
return Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL |
|
||||
Ci.nsIScriptSecurityManager.DONT_REPORT_ERRORS;
|
||||
},
|
||||
|
||||
checkLoadURI: function LinkChecker_checkLoadURI(aURI) {
|
||||
if (!(aURI in this._cache))
|
||||
this._cache[aURI] = this._doCheckLoadURI(aURI);
|
||||
|
||||
return this._cache[aURI];
|
||||
},
|
||||
|
||||
_doCheckLoadURI: function Links_doCheckLoadURI(aURI) {
|
||||
try {
|
||||
Services.scriptSecurityManager.
|
||||
checkLoadURIStrWithPrincipal(gPrincipal, aURI, this.flags);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// We got a weird URI or one that would inherit the caller's principal.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ExpirationFilter = {
|
||||
init: function ExpirationFilter_init() {
|
||||
PageThumbs.addExpirationFilter(this);
|
||||
},
|
||||
|
||||
filterForThumbnailExpiration:
|
||||
function ExpirationFilter_filterForThumbnailExpiration(aCallback) {
|
||||
Links.populateCache(function () {
|
||||
let urls = [];
|
||||
|
||||
// Add all URLs to the list that we want to keep thumbnails for.
|
||||
for (let link of Links.getLinks().slice(0, 25)) {
|
||||
if (link && link.url)
|
||||
urls.push(link.url);
|
||||
}
|
||||
|
||||
aCallback(urls);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that provides the public API of this JSM.
|
||||
*/
|
||||
this.RemoteNewTabUtils = {
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Extract a "site" from a url in a way that multiple urls of a "site" returns
|
||||
* the same "site."
|
||||
* @param aUrl Url spec string
|
||||
* @return The "site" string or null
|
||||
*/
|
||||
extractSite: function Links_extractSite(url) {
|
||||
let host;
|
||||
try {
|
||||
// Note that nsIURI.asciiHost throws NS_ERROR_FAILURE for some types of
|
||||
// URIs, including jar and moz-icon URIs.
|
||||
host = Services.io.newURI(url, null, null).asciiHost;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip off common subdomains of the same site (e.g., www, load balancer)
|
||||
return host.replace(/^(m|mobile|www\d*)\./, "");
|
||||
},
|
||||
|
||||
init: function RemoteNewTabUtils_init() {
|
||||
if (this.initWithoutProviders()) {
|
||||
PlacesProvider.init();
|
||||
Links.addProvider(PlacesProvider);
|
||||
}
|
||||
},
|
||||
|
||||
initWithoutProviders: function RemoteNewTabUtils_initWithoutProviders() {
|
||||
if (!this._initialized) {
|
||||
this._initialized = true;
|
||||
ExpirationFilter.init();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getProviderLinks: function(aProvider) {
|
||||
let cache = Links._providers.get(aProvider);
|
||||
if (cache && cache.sortedLinks) {
|
||||
return cache.sortedLinks;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
isTopSiteGivenProvider: function(aSite, aProvider) {
|
||||
let cache = Links._providers.get(aProvider);
|
||||
if (cache && cache.siteMap) {
|
||||
return cache.siteMap.has(aSite);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isTopPlacesSite: function(aSite) {
|
||||
return this.isTopSiteGivenProvider(aSite, PlacesProvider);
|
||||
},
|
||||
|
||||
links: Links,
|
||||
linkChecker: LinkChecker,
|
||||
placesProvider: PlacesProvider
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'tests/xpcshell/xpcshell.ini',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'NewTabURL.jsm',
|
||||
'PlacesProvider.jsm',
|
||||
'RemoteAboutNewTab.jsm',
|
||||
'RemoteDirectoryLinksProvider.jsm',
|
||||
'RemoteNewTabLocation.jsm',
|
||||
'RemoteNewTabUtils.jsm',
|
||||
]
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
dummy_page.html
|
||||
|
||||
[browser_remotenewtab_pageloads.js]
|
|
@ -0,0 +1,46 @@
|
|||
/* globals XPCOMUtils, Task, RemoteAboutNewTab, RemoteNewTabLocation, ok */
|
||||
"use strict";
|
||||
|
||||
let Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabLocation",
|
||||
"resource:///modules/RemoteNewTabLocation.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteAboutNewTab",
|
||||
"resource:///modules/RemoteAboutNewTab.jsm");
|
||||
|
||||
const TEST_URL = "https://example.com/browser/browser/components/newtab/tests/browser/dummy_page.html";
|
||||
const NEWTAB_URL = "about:remote-newtab";
|
||||
|
||||
let tests = [];
|
||||
|
||||
/*
|
||||
* Tests that:
|
||||
* 1. overriding the RemoteNewTabPageLocation url causes a remote newtab page
|
||||
* to load with the new url.
|
||||
* 2. Messages pass between remote page <--> newTab.js <--> RemoteAboutNewTab.js
|
||||
*/
|
||||
tests.push(Task.spawn(function* testMessage() {
|
||||
yield new Promise(resolve => {
|
||||
RemoteAboutNewTab.pageListener.addMessageListener("NewTab:testMessage", () => {
|
||||
ok(true, "message received");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
add_task(function* open_newtab() {
|
||||
RemoteNewTabLocation.override(TEST_URL);
|
||||
ok(RemoteNewTabLocation.href === TEST_URL, "RemoteNewTabLocation has been overridden");
|
||||
let tabOptions = {
|
||||
gBrowser,
|
||||
url: NEWTAB_URL,
|
||||
};
|
||||
|
||||
for (let test of tests) {
|
||||
yield BrowserTestUtils.withNewTab(tabOptions, function* (browser) { // jshint ignore:line
|
||||
yield test;
|
||||
}); // jshint ignore:line
|
||||
}
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<p>Dummy Page</p>
|
||||
<script type="text/javascript;version=1.8">
|
||||
document.addEventListener("NewTabCommandReady", function readyCmd() {
|
||||
document.removeEventListener("NewTabCommandReady", readyCmd);
|
||||
|
||||
let event = new CustomEvent("NewTabCommand", {
|
||||
detail: {
|
||||
command: "NewTab:testMessage"
|
||||
}
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -20,6 +20,11 @@ add_task(function* () {
|
|||
yield notificationPromise;
|
||||
Assert.ok(!NewTabURL.overridden, "Newtab URL should not be overridden");
|
||||
Assert.equal(NewTabURL.get(), "about:newtab", "Newtab URL should be the about:newtab");
|
||||
|
||||
// change newtab page to remote
|
||||
Services.prefs.setBoolPref("browser.newtabpage.remote", true);
|
||||
Assert.equal(NewTabURL.get(), "about:remote-newtab", "Newtab URL should be the about:remote-newtab");
|
||||
Assert.ok(!NewTabURL.overridden, "Newtab URL should not be overridden");
|
||||
});
|
||||
|
||||
function promiseNewtabURLNotification(aNewURL) {
|
|
@ -0,0 +1,307 @@
|
|||
"use strict";
|
||||
|
||||
/* global XPCOMUtils, PlacesUtils, PlacesTestUtils, PlacesProvider, NetUtil */
|
||||
/* global do_get_profile, run_next_test, add_task */
|
||||
/* global equal, ok */
|
||||
/* exported run_test */
|
||||
|
||||
const {
|
||||
utils: Cu,
|
||||
interfaces: Ci,
|
||||
} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesProvider",
|
||||
"resource:///modules/PlacesProvider.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
// ensure a profile exists
|
||||
do_get_profile();
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/** Test LinkChecker **/
|
||||
|
||||
add_task(function test_LinkChecker_securityCheck() {
|
||||
|
||||
let urls = [
|
||||
{url: "file://home/file/image.png", expected: false},
|
||||
{url: "resource:///modules/PlacesProvider.jsm", expected: false},
|
||||
{url: "javascript:alert('hello')", expected: false}, // jshint ignore:line
|
||||
{url: "data:image/png;base64,XXX", expected: false},
|
||||
{url: "about:newtab", expected: true},
|
||||
{url: "https://example.com", expected: true},
|
||||
{url: "ftp://example.com", expected: true},
|
||||
];
|
||||
for (let {url, expected} of urls) {
|
||||
let observed = PlacesProvider.LinkChecker.checkLoadURI(url);
|
||||
equal(observed , expected, `can load "${url}"?`);
|
||||
}
|
||||
});
|
||||
|
||||
/** Test LinkUtils **/
|
||||
|
||||
add_task(function test_LinkUtils_compareLinks() {
|
||||
|
||||
let fixtures = {
|
||||
firstOlder: {
|
||||
url: "http://www.mozilla.org/firstolder",
|
||||
title: "Mozilla",
|
||||
frecency: 1337,
|
||||
lastVisitDate: 1394678824766431,
|
||||
},
|
||||
older: {
|
||||
url: "http://www.mozilla.org/older",
|
||||
title: "Mozilla",
|
||||
frecency: 1337,
|
||||
lastVisitDate: 1394678824766431,
|
||||
},
|
||||
newer: {
|
||||
url: "http://www.mozilla.org/newer",
|
||||
title: "Mozilla",
|
||||
frecency: 1337,
|
||||
lastVisitDate: 1494678824766431,
|
||||
},
|
||||
moreFrecent: {
|
||||
url: "http://www.mozilla.org/moreFrecent",
|
||||
title: "Mozilla",
|
||||
frecency: 1337357,
|
||||
lastVisitDate: 1394678824766431,
|
||||
}
|
||||
};
|
||||
|
||||
let links = [
|
||||
// tests string ordering, f is before o
|
||||
{link1: fixtures.firstOlder, link2: fixtures.older, expected: false},
|
||||
|
||||
// test identity
|
||||
{link1: fixtures.older, link2: fixtures.older, expected: false},
|
||||
|
||||
// test ordering by date
|
||||
{link1: fixtures.older, link2: fixtures.newer, expected: true},
|
||||
{link1: fixtures.newer, link2: fixtures.older, expected: false},
|
||||
|
||||
// test frecency
|
||||
{link1: fixtures.moreFrecent, link2: fixtures.older, expected: false},
|
||||
];
|
||||
|
||||
for (let {link1, link2, expected} of links) {
|
||||
let observed = PlacesProvider.LinkUtils.compareLinks(link1, link2) > 0;
|
||||
equal(observed , expected, `comparing ${link1.url} and ${link2.url}`);
|
||||
}
|
||||
|
||||
// test error scenarios
|
||||
|
||||
let errorFixtures = {
|
||||
missingFrecency: {
|
||||
url: "http://www.mozilla.org/firstolder",
|
||||
title: "Mozilla",
|
||||
lastVisitDate: 1394678824766431,
|
||||
},
|
||||
missingVisitDate: {
|
||||
url: "http://www.mozilla.org/firstolder",
|
||||
title: "Mozilla",
|
||||
frecency: 1337,
|
||||
},
|
||||
missingURL: {
|
||||
title: "Mozilla",
|
||||
frecency: 1337,
|
||||
lastVisitDate: 1394678824766431,
|
||||
}
|
||||
};
|
||||
|
||||
let errorLinks = [
|
||||
{link1: fixtures.older, link2: errorFixtures.missingFrecency},
|
||||
{link2: fixtures.older, link1: errorFixtures.missingFrecency},
|
||||
{link1: fixtures.older, link2: errorFixtures.missingVisitDate},
|
||||
{link1: fixtures.older, link2: errorFixtures.missingURL},
|
||||
{link1: errorFixtures.missingFrecency, link2: errorFixtures.missingVisitDate}
|
||||
];
|
||||
|
||||
let errorCount = 0;
|
||||
for (let {link1, link2} of errorLinks) {
|
||||
try {
|
||||
let observed = PlacesProvider.LinkUtils.compareLinks(link1, link2) > 0; // jshint ignore:line
|
||||
} catch (e) {
|
||||
ok(true, `exception for comparison of ${link1.url} and ${link2.url}`);
|
||||
errorCount += 1;
|
||||
}
|
||||
}
|
||||
equal(errorCount, errorLinks.length);
|
||||
});
|
||||
|
||||
/** Test Provider **/
|
||||
|
||||
add_task(function* test_Links_getLinks() {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
let provider = PlacesProvider.links;
|
||||
|
||||
let links = yield provider.getLinks();
|
||||
equal(links.length, 0, "empty history yields empty links");
|
||||
|
||||
// add a visit
|
||||
let testURI = NetUtil.newURI("http://mozilla.com");
|
||||
yield PlacesTestUtils.addVisits(testURI);
|
||||
|
||||
links = yield provider.getLinks();
|
||||
equal(links.length, 1, "adding a visit yields a link");
|
||||
equal(links[0].url, testURI.spec, "added visit corresponds to added url");
|
||||
});
|
||||
|
||||
add_task(function* test_Links_getLinks_Order() {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
let provider = PlacesProvider.links;
|
||||
let {
|
||||
TRANSITION_TYPED,
|
||||
TRANSITION_LINK
|
||||
} = PlacesUtils.history;
|
||||
|
||||
function timeDaysAgo(numDays) {
|
||||
let now = new Date();
|
||||
return now.getTime() - (numDays * 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
let timeEarlier = timeDaysAgo(0);
|
||||
let timeLater = timeDaysAgo(2);
|
||||
|
||||
let visits = [
|
||||
// frecency 200
|
||||
{uri: NetUtil.newURI("https://mozilla.com/0"), visitDate: timeEarlier, transition: TRANSITION_TYPED},
|
||||
// sort by url, frecency 200
|
||||
{uri: NetUtil.newURI("https://mozilla.com/1"), visitDate: timeEarlier, transition: TRANSITION_TYPED},
|
||||
// sort by last visit date, frecency 200
|
||||
{uri: NetUtil.newURI("https://mozilla.com/2"), visitDate: timeLater, transition: TRANSITION_TYPED},
|
||||
// sort by frecency, frecency 10
|
||||
{uri: NetUtil.newURI("https://mozilla.com/3"), visitDate: timeLater, transition: TRANSITION_LINK},
|
||||
];
|
||||
|
||||
let links = yield provider.getLinks();
|
||||
equal(links.length, 0, "empty history yields empty links");
|
||||
yield PlacesTestUtils.addVisits(visits);
|
||||
|
||||
links = yield provider.getLinks();
|
||||
equal(links.length, visits.length, "number of links added is the same as obtain by getLinks");
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
equal(links[i].url, visits[i].uri.spec, "links are obtained in the expected order");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_Links_onLinkChanged() {
|
||||
let provider = PlacesProvider.links;
|
||||
provider.init();
|
||||
|
||||
let url = "https://example.com/onFrecencyChanged1";
|
||||
let linkChangedMsgCount = 0;
|
||||
|
||||
let linkChangedPromise = new Promise(resolve => {
|
||||
let handler = (_, link) => { // jshint ignore:line
|
||||
/* There are 3 linkChanged events:
|
||||
* 1. visit insertion (-1 frecency by default)
|
||||
* 2. frecency score update (after transition type calculation etc)
|
||||
* 3. title change
|
||||
*/
|
||||
if (link.url === url) {
|
||||
equal(link.url, url, `expected url on linkChanged event`);
|
||||
linkChangedMsgCount += 1;
|
||||
if (linkChangedMsgCount === 3) {
|
||||
ok(true, `all linkChanged events captured`);
|
||||
provider.off("linkChanged", this);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
provider.on("linkChanged", handler);
|
||||
});
|
||||
|
||||
// add a visit
|
||||
let testURI = NetUtil.newURI(url);
|
||||
yield PlacesTestUtils.addVisits(testURI);
|
||||
yield linkChangedPromise;
|
||||
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
provider.destroy();
|
||||
});
|
||||
|
||||
add_task(function* test_Links_onClearHistory() {
|
||||
let provider = PlacesProvider.links;
|
||||
provider.init();
|
||||
|
||||
let clearHistoryPromise = new Promise(resolve => {
|
||||
let handler = () => {
|
||||
ok(true, `clearHistory event captured`);
|
||||
provider.off("clearHistory", handler);
|
||||
resolve();
|
||||
};
|
||||
provider.on("clearHistory", handler);
|
||||
});
|
||||
|
||||
// add visits
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
let url = `https://example.com/onClearHistory${i}`;
|
||||
let testURI = NetUtil.newURI(url);
|
||||
yield PlacesTestUtils.addVisits(testURI);
|
||||
}
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
yield clearHistoryPromise;
|
||||
provider.destroy();
|
||||
});
|
||||
|
||||
add_task(function* test_Links_onDeleteURI() {
|
||||
let provider = PlacesProvider.links;
|
||||
provider.init();
|
||||
|
||||
let testURL = "https://example.com/toDelete";
|
||||
|
||||
let deleteURIPromise = new Promise(resolve => {
|
||||
let handler = (_, {url}) => { // jshint ignore:line
|
||||
equal(testURL, url, "deleted url and expected url are the same");
|
||||
provider.off("deleteURI", handler);
|
||||
resolve();
|
||||
};
|
||||
|
||||
provider.on("deleteURI", handler);
|
||||
});
|
||||
|
||||
let testURI = NetUtil.newURI(testURL);
|
||||
yield PlacesTestUtils.addVisits(testURI);
|
||||
yield PlacesUtils.history.remove(testURL);
|
||||
yield deleteURIPromise;
|
||||
provider.destroy();
|
||||
});
|
||||
|
||||
add_task(function* test_Links_onManyLinksChanged() {
|
||||
let provider = PlacesProvider.links;
|
||||
provider.init();
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
let handler = () => {
|
||||
ok(true);
|
||||
provider.off("manyLinksChanged", handler);
|
||||
resolve();
|
||||
};
|
||||
|
||||
provider.on("manyLinksChanged", handler);
|
||||
});
|
||||
|
||||
let testURL = "https://example.com/toDelete";
|
||||
let testURI = NetUtil.newURI(testURL);
|
||||
yield PlacesTestUtils.addVisits(testURI);
|
||||
|
||||
// trigger DecayFrecency
|
||||
PlacesUtils.history.QueryInterface(Ci.nsIObserver).
|
||||
observe(null, "idle-daily", "");
|
||||
|
||||
yield promise;
|
||||
provider.destroy();
|
||||
});
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,40 @@
|
|||
/* globals ok, equal, RemoteNewTabLocation, Services */
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource:///modules/RemoteNewTabLocation.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.importGlobalProperties(["URL"]);
|
||||
|
||||
add_task(function* () {
|
||||
var notificationPromise;
|
||||
let defaultHref = RemoteNewTabLocation.href;
|
||||
|
||||
ok(RemoteNewTabLocation.href, "Default location has an href");
|
||||
ok(RemoteNewTabLocation.origin, "Default location has an origin");
|
||||
ok(!RemoteNewTabLocation.overridden, "Default location is not overridden");
|
||||
|
||||
let testURL = new URL("https://example.com/");
|
||||
|
||||
notificationPromise = changeNotificationPromise(testURL.href);
|
||||
RemoteNewTabLocation.override(testURL.href);
|
||||
yield notificationPromise;
|
||||
ok(RemoteNewTabLocation.overridden, "Remote location should be overridden");
|
||||
equal(RemoteNewTabLocation.href, testURL.href, "Remote href should be the custom URL");
|
||||
equal(RemoteNewTabLocation.origin, testURL.origin, "Remote origin should be the custom URL");
|
||||
|
||||
notificationPromise = changeNotificationPromise(defaultHref);
|
||||
RemoteNewTabLocation.reset();
|
||||
yield notificationPromise;
|
||||
ok(!RemoteNewTabLocation.overridden, "Newtab URL should not be overridden");
|
||||
equal(RemoteNewTabLocation.href, defaultHref, "Remote href should be reset");
|
||||
});
|
||||
|
||||
function changeNotificationPromise(aNewURL) {
|
||||
return new Promise(resolve => {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint ignore:line
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
equal(aData, aNewURL, "remote-new-tab-location-changed data should be new URL.");
|
||||
resolve();
|
||||
}, "remote-new-tab-location-changed", false);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// See also browser/base/content/test/newtab/.
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
Cu.import("resource:///modules/RemoteNewTabUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function validCacheMidPopulation() {
|
||||
let expectedLinks = makeLinks(0, 3, 1);
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
let promise = new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
// isTopSiteGivenProvider() and getProviderLinks() should still return results
|
||||
// even when cache is empty or being populated.
|
||||
do_check_false(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider));
|
||||
do_check_links(RemoteNewTabUtils.getProviderLinks(provider), []);
|
||||
|
||||
yield promise;
|
||||
|
||||
// Once the cache is populated, we get the expected results
|
||||
do_check_true(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider));
|
||||
do_check_links(RemoteNewTabUtils.getProviderLinks(provider), expectedLinks);
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function notifyLinkDelete() {
|
||||
let expectedLinks = makeLinks(0, 3, 1);
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Remove a link.
|
||||
let removedLink = expectedLinks[2];
|
||||
provider.notifyLinkChanged(removedLink, 2, true);
|
||||
let links = RemoteNewTabUtils.links._providers.get(provider);
|
||||
|
||||
// Check that sortedLinks is correctly updated.
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks.slice(0, 2));
|
||||
|
||||
// Check that linkMap is accurately updated.
|
||||
do_check_eq(links.linkMap.size, 2);
|
||||
do_check_true(links.linkMap.get(expectedLinks[0].url));
|
||||
do_check_true(links.linkMap.get(expectedLinks[1].url));
|
||||
do_check_false(links.linkMap.get(removedLink.url));
|
||||
|
||||
// Check that siteMap is correctly updated.
|
||||
do_check_eq(links.siteMap.size, 2);
|
||||
do_check_true(links.siteMap.has(RemoteNewTabUtils.extractSite(expectedLinks[0].url)));
|
||||
do_check_true(links.siteMap.has(RemoteNewTabUtils.extractSite(expectedLinks[1].url)));
|
||||
do_check_false(links.siteMap.has(RemoteNewTabUtils.extractSite(removedLink.url)));
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function populatePromise() {
|
||||
let count = 0;
|
||||
let expectedLinks = makeLinks(0, 10, 2);
|
||||
|
||||
let getLinksFcn = Task.async(function* (callback) {
|
||||
//Should not be calling getLinksFcn twice
|
||||
count++;
|
||||
do_check_eq(count, 1);
|
||||
yield Promise.resolve();
|
||||
callback(expectedLinks);
|
||||
});
|
||||
|
||||
let provider = new TestProvider(getLinksFcn);
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
RemoteNewTabUtils.links.populateProviderCache(provider, () => {});
|
||||
RemoteNewTabUtils.links.populateProviderCache(provider, () => {
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function isTopSiteGivenProvider() {
|
||||
let expectedLinks = makeLinks(0, 10, 2);
|
||||
|
||||
// The lowest 2 frecencies have the same base domain.
|
||||
expectedLinks[expectedLinks.length - 2].url = expectedLinks[expectedLinks.length - 1].url + "Test";
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider), false);
|
||||
|
||||
// Push out frecency 2 because the maxNumLinks is reached when adding frecency 3
|
||||
let newLink = makeLink(3);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// There is still a frecent url with example2 domain, so it's still frecent.
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example3.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
|
||||
|
||||
// Push out frecency 3
|
||||
newLink = makeLink(5);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// Push out frecency 4
|
||||
newLink = makeLink(9);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// Our count reached 0 for the example2.com domain so it's no longer a frecent site.
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example5.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), false);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function multipleProviders() {
|
||||
// Make each provider generate RemoteNewTabUtils.links.maxNumLinks links to check
|
||||
// that no more than maxNumLinks are actually returned in the merged list.
|
||||
let evenLinks = makeLinks(0, 2 * RemoteNewTabUtils.links.maxNumLinks, 2);
|
||||
let evenProvider = new TestProvider(done => done(evenLinks));
|
||||
let oddLinks = makeLinks(0, 2 * RemoteNewTabUtils.links.maxNumLinks - 1, 2);
|
||||
let oddProvider = new TestProvider(done => done(oddLinks));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(evenProvider);
|
||||
RemoteNewTabUtils.links.addProvider(oddProvider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
let links = RemoteNewTabUtils.links.getLinks();
|
||||
let expectedLinks = makeLinks(RemoteNewTabUtils.links.maxNumLinks,
|
||||
2 * RemoteNewTabUtils.links.maxNumLinks,
|
||||
1);
|
||||
do_check_eq(links.length, RemoteNewTabUtils.links.maxNumLinks);
|
||||
do_check_links(links, expectedLinks);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(evenProvider);
|
||||
RemoteNewTabUtils.links.removeProvider(oddProvider);
|
||||
});
|
||||
|
||||
add_task(function changeLinks() {
|
||||
let expectedLinks = makeLinks(0, 20, 2);
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a new link.
|
||||
let newLink = makeLink(19);
|
||||
expectedLinks.splice(1, 0, newLink);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a link that's changed sort criteria.
|
||||
newLink.frecency = 17;
|
||||
expectedLinks.splice(1, 1);
|
||||
expectedLinks.splice(2, 0, newLink);
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
frecency: 17,
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a link that's changed title.
|
||||
newLink.title = "My frecency is now 17";
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
title: newLink.title,
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a new link again, but this time make it overflow maxNumLinks.
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
newLink = makeLink(21);
|
||||
expectedLinks.unshift(newLink);
|
||||
expectedLinks.pop();
|
||||
do_check_eq(expectedLinks.length, provider.maxNumLinks); // Sanity check.
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of many links changed.
|
||||
expectedLinks = makeLinks(0, 3, 1);
|
||||
provider.notifyManyLinksChanged();
|
||||
|
||||
// Since _populateProviderCache() is async, we must wait until the provider's
|
||||
// populate promise has been resolved.
|
||||
yield RemoteNewTabUtils.links._providers.get(provider).populatePromise;
|
||||
|
||||
// RemoteNewTabUtils.links will now repopulate its cache
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function oneProviderAlreadyCached() {
|
||||
let links1 = makeLinks(0, 10, 1);
|
||||
let provider1 = new TestProvider(done => done(links1));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider1);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links1);
|
||||
|
||||
let links2 = makeLinks(10, 20, 1);
|
||||
let provider2 = new TestProvider(done => done(links2));
|
||||
RemoteNewTabUtils.links.addProvider(provider2);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links2.concat(links1));
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider1);
|
||||
RemoteNewTabUtils.links.removeProvider(provider2);
|
||||
});
|
||||
|
||||
add_task(function newLowRankedLink() {
|
||||
// Init a provider with 10 links and make its maximum number also 10.
|
||||
let links = makeLinks(0, 10, 1);
|
||||
let provider = new TestProvider(done => done(links));
|
||||
provider.maxNumLinks = links.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
// Notify of a new link that's low-ranked enough not to make the list.
|
||||
let newLink = makeLink(0);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
// Notify about the new link's title change.
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
title: "a new title",
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function extractSite() {
|
||||
// All these should extract to the same site
|
||||
[ "mozilla.org",
|
||||
"m.mozilla.org",
|
||||
"mobile.mozilla.org",
|
||||
"www.mozilla.org",
|
||||
"www3.mozilla.org",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_eq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted same " + host);
|
||||
});
|
||||
|
||||
// All these should extract to the same subdomain
|
||||
[ "bugzilla.mozilla.org",
|
||||
"www.bugzilla.mozilla.org",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_eq(RemoteNewTabUtils.extractSite(url), "bugzilla.mozilla.org", "extracted eTLD+2 " + host);
|
||||
});
|
||||
|
||||
// All these should not extract to the same site
|
||||
[ "bugzilla.mozilla.org",
|
||||
"bug123.bugzilla.mozilla.org",
|
||||
"too.many.levels.bugzilla.mozilla.org",
|
||||
"m2.mozilla.org",
|
||||
"mobile30.mozilla.org",
|
||||
"ww.mozilla.org",
|
||||
"ww2.mozilla.org",
|
||||
"wwwww.mozilla.org",
|
||||
"wwwww50.mozilla.org",
|
||||
"wwws.mozilla.org",
|
||||
"secure.mozilla.org",
|
||||
"secure10.mozilla.org",
|
||||
"many.levels.deep.mozilla.org",
|
||||
"just.check.in",
|
||||
"192.168.0.1",
|
||||
"localhost",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_neq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted diff " + host);
|
||||
});
|
||||
|
||||
// All these should not extract to the same site
|
||||
[ "about:blank",
|
||||
"file:///Users/user/file",
|
||||
"chrome://browser/something",
|
||||
"ftp://ftp.mozilla.org/",
|
||||
].forEach(url => {
|
||||
do_check_neq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted diff url " + url);
|
||||
});
|
||||
});
|
||||
|
||||
function TestProvider(getLinksFn) {
|
||||
this.getLinks = getLinksFn;
|
||||
this._observers = new Set();
|
||||
}
|
||||
|
||||
TestProvider.prototype = {
|
||||
addObserver: function (observer) {
|
||||
this._observers.add(observer);
|
||||
},
|
||||
notifyLinkChanged: function (link, index=-1, deleted=false) {
|
||||
this._notifyObservers("onLinkChanged", link, index, deleted);
|
||||
},
|
||||
notifyManyLinksChanged: function () {
|
||||
this._notifyObservers("onManyLinksChanged");
|
||||
},
|
||||
_notifyObservers: function () {
|
||||
let observerMethodName = arguments[0];
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(this);
|
||||
for (let obs of this._observers) {
|
||||
if (obs[observerMethodName])
|
||||
obs[observerMethodName].apply(RemoteNewTabUtils.links, args);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function do_check_links(actualLinks, expectedLinks) {
|
||||
do_check_true(Array.isArray(actualLinks));
|
||||
do_check_eq(actualLinks.length, expectedLinks.length);
|
||||
for (let i = 0; i < expectedLinks.length; i++) {
|
||||
let expected = expectedLinks[i];
|
||||
let actual = actualLinks[i];
|
||||
do_check_eq(actual.url, expected.url);
|
||||
do_check_eq(actual.title, expected.title);
|
||||
do_check_eq(actual.frecency, expected.frecency);
|
||||
do_check_eq(actual.lastVisitDate, expected.lastVisitDate);
|
||||
}
|
||||
}
|
||||
|
||||
function makeLinks(frecRangeStart, frecRangeEnd, step) {
|
||||
let links = [];
|
||||
// Remember, links are ordered by frecency descending.
|
||||
for (let i = frecRangeEnd; i > frecRangeStart; i -= step) {
|
||||
links.push(makeLink(i));
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
function makeLink(frecency) {
|
||||
return {
|
||||
url: "http://example" + frecency + ".com/",
|
||||
title: "My frecency is " + frecency,
|
||||
frecency: frecency,
|
||||
lastVisitDate: 0,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
[test_NewTabURL.js]
|
||||
[test_PlacesProvider.js]
|
||||
[test_RemoteDirectoryLinksProvider.js]
|
||||
[test_RemoteNewTabLocation.js]
|
||||
[test_RemoteNewTabUtils.js]
|
|
@ -20,6 +20,21 @@ XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "AboutNewTab",
|
||||
"resource:///modules/AboutNewTab.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider",
|
||||
"resource:///modules/DirectoryLinksProvider.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteAboutNewTab",
|
||||
"resource:///modules/RemoteAboutNewTab.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteDirectoryLinksProvider",
|
||||
"resource:///modules/RemoteDirectoryLinksProvider.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
|
||||
|
@ -29,9 +44,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ContentClick",
|
||||
"resource:///modules/ContentClick.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider",
|
||||
"resource:///modules/DirectoryLinksProvider.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
|
@ -53,9 +65,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader",
|
||||
"resource:///modules/CustomizationTabPreloader.jsm");
|
||||
|
||||
|
@ -591,7 +600,7 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// initialization (called on application startup)
|
||||
// initialization (called on application startup)
|
||||
_init: function BG__init() {
|
||||
let os = Services.obs;
|
||||
os.addObserver(this, "notifications-open-settings", false);
|
||||
|
@ -811,7 +820,7 @@ BrowserGlue.prototype = {
|
|||
this._sanitizer.onStartup();
|
||||
// check if we're in safe mode
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
|
||||
Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul",
|
||||
"_blank", "chrome,centerscreen,modal,resizable=no", null);
|
||||
}
|
||||
|
||||
|
@ -826,9 +835,6 @@ BrowserGlue.prototype = {
|
|||
|
||||
WebappManager.init();
|
||||
PageThumbs.init();
|
||||
NewTabUtils.init();
|
||||
DirectoryLinksProvider.init();
|
||||
NewTabUtils.links.addProvider(DirectoryLinksProvider);
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (Services.prefs.getBoolPref("dom.identity.enabled")) {
|
||||
SignInToWebsiteUX.init();
|
||||
|
@ -836,7 +842,17 @@ BrowserGlue.prototype = {
|
|||
#endif
|
||||
webrtcUI.init();
|
||||
AboutHome.init();
|
||||
|
||||
RemoteDirectoryLinksProvider.init();
|
||||
RemoteNewTabUtils.init();
|
||||
RemoteNewTabUtils.links.addProvider(RemoteDirectoryLinksProvider);
|
||||
RemoteAboutNewTab.init();
|
||||
|
||||
DirectoryLinksProvider.init();
|
||||
NewTabUtils.init();
|
||||
NewTabUtils.links.addProvider(DirectoryLinksProvider);
|
||||
AboutNewTab.init();
|
||||
|
||||
SessionStore.init();
|
||||
BrowserUITelemetry.init();
|
||||
ContentSearch.init();
|
||||
|
@ -1161,6 +1177,8 @@ BrowserGlue.prototype = {
|
|||
|
||||
CustomizationTabPreloader.uninit();
|
||||
WebappManager.uninit();
|
||||
|
||||
RemoteAboutNewTab.uninit();
|
||||
AboutNewTab.uninit();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (Services.prefs.getBoolPref("dom.identity.enabled")) {
|
||||
|
@ -2043,7 +2061,7 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
|
||||
if (currentUIVersion < 19) {
|
||||
let detector = null;
|
||||
let detector = null;
|
||||
try {
|
||||
detector = Services.prefs.getComplexValue("intl.charset.detector",
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
|
@ -2205,7 +2223,7 @@ BrowserGlue.prototype = {
|
|||
|
||||
Services.prefs.clearUserPref("browser.devedition.showCustomizeButton");
|
||||
}
|
||||
|
||||
|
||||
if (currentUIVersion < 31) {
|
||||
xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
|
||||
xulStore.removeValue(BROWSER_DOCURL, "home-button", "class");
|
||||
|
|
|
@ -30,7 +30,6 @@ EXTRA_JS_MODULES += [
|
|||
'FormValidationHandler.jsm',
|
||||
'HiddenFrame.jsm',
|
||||
'NetworkPrioritizer.jsm',
|
||||
'NewTabURL.jsm',
|
||||
'offlineAppCache.jsm',
|
||||
'PanelFrame.jsm',
|
||||
'PluginContent.jsm',
|
||||
|
|
|
@ -5,5 +5,4 @@ firefox-appdir = browser
|
|||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
[test_DirectoryLinksProvider.js]
|
||||
[test_NewTabURL.js]
|
||||
[test_SitePermissions.js]
|
||||
|
|
|
@ -21,15 +21,15 @@
|
|||
:root {
|
||||
--backbutton-urlbar-overlap: 6px;
|
||||
|
||||
--toolbarbutton-hover-background: hsla(0,0%,100%,.3) linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.2));
|
||||
--toolbarbutton-hover-boxshadow: 0 1px 0 hsla(0,0%,100%,.3) inset, 0 0 0 1px hsla(0,0%,100%,.2) inset, 0 1px 0 hsla(0,0%,0%,.03);
|
||||
--toolbarbutton-hover-bordercolor: rgb(154,154,154);
|
||||
--toolbarbutton-hover-background: rgba(255,255,255,.5) linear-gradient(rgba(255,255,255,.5), transparent);
|
||||
--toolbarbutton-hover-bordercolor: rgba(0,0,0,.25);
|
||||
--toolbarbutton-hover-boxshadow: none;
|
||||
|
||||
--toolbarbutton-active-boxshadow: 0 1px 1px hsla(0,0%,0%,.1) inset, 0 0 1px hsla(0,0%,0%,.3) inset;
|
||||
--toolbarbutton-active-bordercolor: rgb(154,154,154);
|
||||
--toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4));
|
||||
--toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.4));
|
||||
--toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
|
||||
--toolbarbutton-active-boxshadow: 0 1px 1px rgba(0,0,0,.1) inset, 0 0 1px rgba(0,0,0,.3) inset;
|
||||
|
||||
--toolbarbutton-checkedhover-backgroundcolor: rgba(154,154,154,.15);
|
||||
--toolbarbutton-checkedhover-backgroundcolor: rgba(200,200,200,.5);
|
||||
|
||||
--identity-box-verified-background-color: #fff;
|
||||
|
||||
|
@ -626,8 +626,7 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-stack,
|
||||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-icon {
|
||||
background: var(--toolbarbutton-hover-background);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
background-clip: padding-box;
|
||||
border-color: var(--toolbarbutton-hover-bordercolor);
|
||||
box-shadow: var(--toolbarbutton-hover-boxshadow);
|
||||
}
|
||||
|
@ -644,9 +643,8 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack,
|
||||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon {
|
||||
background: var(--toolbarbutton-active-background);
|
||||
background-clip: padding-box;
|
||||
box-shadow: var(--toolbarbutton-active-boxshadow);
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--toolbarbutton-active-bordercolor);
|
||||
transition-duration: 10ms;
|
||||
}
|
||||
|
|
|
@ -3244,26 +3244,26 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
|
|||
bool value;
|
||||
nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
|
||||
if (parentAsDocShell) {
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
|
||||
if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
|
||||
SetAllowPlugins(value);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
|
||||
if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
|
||||
SetAllowJavascript(value);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
|
||||
if (mAllowMetaRedirects && NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
|
||||
SetAllowMetaRedirects(value);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
|
||||
if (mAllowSubframes && NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
|
||||
SetAllowSubframes(value);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
|
||||
if (mAllowImages && NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
|
||||
SetAllowImages(value);
|
||||
}
|
||||
SetAllowMedia(parentAsDocShell->GetAllowMedia());
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
|
||||
SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
|
||||
if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
|
||||
SetAllowWindowControl(value);
|
||||
}
|
||||
SetAllowContentRetargeting(
|
||||
SetAllowContentRetargeting(mAllowContentRetargeting &&
|
||||
parentAsDocShell->GetAllowContentRetargetingOnChildren());
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
|
||||
SetIsActive(value);
|
||||
|
@ -3274,7 +3274,7 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
|
|||
if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
|
||||
value = false;
|
||||
}
|
||||
SetAllowDNSPrefetch(value);
|
||||
SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
|
||||
value = parentAsDocShell->GetAffectPrivateSessionLifetime();
|
||||
SetAffectPrivateSessionLifetime(value);
|
||||
uint32_t flags;
|
||||
|
|
|
@ -723,6 +723,7 @@ GK_ATOM(onconfigurationchange, "onconfigurationchange")
|
|||
GK_ATOM(onconnect, "onconnect")
|
||||
GK_ATOM(onconnected, "onconnected")
|
||||
GK_ATOM(onconnecting, "onconnecting")
|
||||
GK_ATOM(onconnectionavailable, "onconnectionavailable")
|
||||
GK_ATOM(onconnectionstatechanged, "onconnectionstatechanged")
|
||||
GK_ATOM(oncontextmenu, "oncontextmenu")
|
||||
GK_ATOM(oncopy, "oncopy")
|
||||
|
@ -898,8 +899,6 @@ GK_ATOM(onselectionchange, "onselectionchange")
|
|||
GK_ATOM(onselectstart, "onselectstart")
|
||||
GK_ATOM(onsending, "onsending")
|
||||
GK_ATOM(onsent, "onsent")
|
||||
GK_ATOM(onsessionavailable, "onsessionavailable")
|
||||
GK_ATOM(onsessionconnect, "onsessionconnect")
|
||||
GK_ATOM(onset, "onset")
|
||||
GK_ATOM(onshow, "onshow")
|
||||
GK_ATOM(onshutter, "onshutter")
|
||||
|
|
|
@ -7,10 +7,11 @@ var LB;
|
|||
|
||||
function run_test() {
|
||||
|
||||
if(("@mozilla.org/windows-registry-key;1" in C) || ("nsILocalFileOS2" in I))
|
||||
if (mozinfo.os == "win") {
|
||||
LB = "\r\n";
|
||||
else
|
||||
} else {
|
||||
LB = "\n";
|
||||
}
|
||||
|
||||
for (var i = 0; i < tests.length && tests[i]; ++i) {
|
||||
tests[i].call();
|
||||
|
|
|
@ -183,7 +183,7 @@ ContentBridgeParent::GetCPOWManager()
|
|||
if (ManagedPJavaScriptParent().Length()) {
|
||||
return CPOWManagerFor(ManagedPJavaScriptParent()[0]);
|
||||
}
|
||||
return CPOWManagerFor(SendPJavaScriptConstructor());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -41,6 +41,8 @@ parent:
|
|||
sync SyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal)
|
||||
returns (StructuredCloneData[] retval);
|
||||
|
||||
async PJavaScript();
|
||||
both:
|
||||
// Both the parent and the child can construct the PBrowser.
|
||||
// See the comment in PContent::PBrowser().
|
||||
|
@ -49,8 +51,6 @@ both:
|
|||
|
||||
async PBlob(BlobConstructorParams params);
|
||||
|
||||
async PJavaScript();
|
||||
|
||||
AsyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal);
|
||||
};
|
||||
|
|
|
@ -54,8 +54,6 @@ public:
|
|||
|
||||
virtual bool OnStateMachineTaskQueue() const = 0;
|
||||
|
||||
virtual bool OnDecodeTaskQueue() const = 0;
|
||||
|
||||
// Get the current MediaResource being used. Its URI will be returned
|
||||
// by currentSrc. Returns what was passed to Load(), if Load() has been called.
|
||||
virtual MediaResource* GetResource() const = 0;
|
||||
|
@ -124,10 +122,6 @@ public:
|
|||
// Called from HTMLMediaElement when owner document activity changes
|
||||
virtual void SetElementVisibility(bool aIsVisible) {}
|
||||
|
||||
// Called by some MediaDecoderReader to determine if we can rely
|
||||
// on the resource length to limit reads.
|
||||
virtual bool HasInitializationData() { return false; }
|
||||
|
||||
// Stack based class to assist in notifying the frame statistics of
|
||||
// parsed and decoded frames. Use inside video demux & decode functions
|
||||
// to ensure all parsed and decoded frames are reported on all return paths.
|
||||
|
|
|
@ -1238,12 +1238,6 @@ MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
|
|||
mPreservesPitch = aPreservesPitch;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoder::OnDecodeTaskQueue() const {
|
||||
NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
|
||||
return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeTaskQueue() : false;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
|
||||
{
|
||||
|
|
|
@ -519,8 +519,6 @@ public:
|
|||
|
||||
bool OnStateMachineTaskQueue() const override;
|
||||
|
||||
bool OnDecodeTaskQueue() const override;
|
||||
|
||||
MediaDecoderStateMachine* GetStateMachine() const;
|
||||
void SetStateMachine(MediaDecoderStateMachine* aStateMachine);
|
||||
|
||||
|
|
|
@ -2864,11 +2864,6 @@ MediaDecoderStateMachine::ScheduleStateMachineIn(int64_t aMicroseconds)
|
|||
mDelayedScheduler.Ensure(target);
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::OnDecodeTaskQueue() const
|
||||
{
|
||||
return !DecodeTaskQueue() || DecodeTaskQueue()->IsCurrentThreadIn();
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::OnTaskQueue() const
|
||||
{
|
||||
return OwnerThread()->IsCurrentThreadIn();
|
||||
|
|
|
@ -235,8 +235,6 @@ public:
|
|||
|
||||
// Functions used by assertions to ensure we're calling things
|
||||
// on the appropriate threads.
|
||||
bool OnDecodeTaskQueue() const;
|
||||
|
||||
bool OnTaskQueue() const;
|
||||
|
||||
size_t SizeOfVideoQueue() {
|
||||
|
|
|
@ -98,18 +98,17 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
|
|||
// etc output).
|
||||
const bool isMP4Audio = aMIMETypeExcludingCodecs.EqualsASCII("audio/mp4") ||
|
||||
aMIMETypeExcludingCodecs.EqualsASCII("audio/x-m4a");
|
||||
const bool isMP4Video = aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") ||
|
||||
aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v");
|
||||
const bool isMP4Video =
|
||||
// On B2G, treat 3GPP as MP4 when Gonk PDM is available.
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP) ||
|
||||
#endif
|
||||
aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") ||
|
||||
aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v");
|
||||
if (!isMP4Audio && !isMP4Video) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MOZ_GONK_MEDIACODEC
|
||||
if (aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP)) {
|
||||
return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsTArray<nsCString> codecMimes;
|
||||
if (aCodecs.IsEmpty()) {
|
||||
// No codecs specified. Assume AAC/H.264
|
||||
|
|
|
@ -38,7 +38,7 @@ struct AudioTimelineEvent final
|
|||
};
|
||||
|
||||
AudioTimelineEvent(Type aType, double aTime, float aValue, double aTimeConstant = 0.0,
|
||||
float aDuration = 0.0, const float* aCurve = nullptr,
|
||||
double aDuration = 0.0, const float* aCurve = nullptr,
|
||||
uint32_t aCurveLength = 0)
|
||||
: mType(aType)
|
||||
, mTimeConstant(aTimeConstant)
|
||||
|
@ -209,6 +209,8 @@ public:
|
|||
// curve event.
|
||||
for (unsigned i = 0; i < mEvents.Length(); ++i) {
|
||||
if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve &&
|
||||
!(aEvent.mType == AudioTimelineEvent::SetValueCurve &&
|
||||
aEvent.template Time<double>() == mEvents[i].template Time<double>()) &&
|
||||
mEvents[i].template Time<double>() <= aEvent.template Time<double>() &&
|
||||
(mEvents[i].template Time<double>() + mEvents[i].mDuration) >= aEvent.template Time<double>()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
|
@ -220,6 +222,11 @@ public:
|
|||
// events.
|
||||
if (aEvent.mType == AudioTimelineEvent::SetValueCurve) {
|
||||
for (unsigned i = 0; i < mEvents.Length(); ++i) {
|
||||
// In case we have two curve at the same time
|
||||
if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve &&
|
||||
mEvents[i].template Time<double>() == aEvent.template Time<double>()) {
|
||||
continue;
|
||||
}
|
||||
if (mEvents[i].template Time<double>() > aEvent.template Time<double>() &&
|
||||
mEvents[i].template Time<double>() < (aEvent.template Time<double>() + aEvent.mDuration)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
|
|
|
@ -40,13 +40,6 @@ BufferDecoder::OnStateMachineTaskQueue() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BufferDecoder::OnDecodeTaskQueue() const
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueueIdentity, "Forgot to call BeginDecoding?");
|
||||
return mTaskQueueIdentity->IsCurrentThreadIn();
|
||||
}
|
||||
|
||||
MediaResource*
|
||||
BufferDecoder::GetResource() const
|
||||
{
|
||||
|
|
|
@ -33,8 +33,6 @@ public:
|
|||
|
||||
virtual bool OnStateMachineTaskQueue() const final override;
|
||||
|
||||
virtual bool OnDecodeTaskQueue() const final override;
|
||||
|
||||
virtual MediaResource* GetResource() const final override;
|
||||
|
||||
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override;
|
||||
|
|
|
@ -145,7 +145,8 @@ void TestSpecExample()
|
|||
is(timeline.GetValueAtTime(0.6), 0.75f, "Correct value");
|
||||
is(timeline.GetValueAtTime(0.65), (0.75f * powf(0.05f / 0.75f, 0.5f)), "Correct value");
|
||||
is(timeline.GetValueAtTime(0.7), -1.0f, "Correct value");
|
||||
is(timeline.GetValueAtTime(0.9), 0.0f, "Correct value");
|
||||
is(timeline.GetValueAtTime(0.8), 0.0f, "Correct value");
|
||||
is(timeline.GetValueAtTime(0.9), 1.0f, "Correct value");
|
||||
is(timeline.GetValueAtTime(1.0), 1.0f, "Correct value");
|
||||
}
|
||||
|
||||
|
@ -431,6 +432,19 @@ void TestExponentialInvalidPreviousZeroValue()
|
|||
is(rv, NS_OK, "Should succeed this time");
|
||||
}
|
||||
|
||||
void
|
||||
TestSettingValueCurveTwice()
|
||||
{
|
||||
Timeline timeline(0.f);
|
||||
float curve[] = { -1.0f, 0.0f, 1.0f };
|
||||
|
||||
ErrorResultMock rv;
|
||||
|
||||
timeline.SetValueCurveAtTime(curve, ArrayLength(curve), 0.0f, 0.3f, rv);
|
||||
timeline.SetValueCurveAtTime(curve, ArrayLength(curve), 0.0f, 0.3f, rv);
|
||||
is(rv, NS_OK, "SetValueCurveAtTime succeeded");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ScopedXPCOM xpcom("TestAudioEventTimeline");
|
||||
|
@ -456,6 +470,7 @@ int main()
|
|||
TestExponentialRampAtSameTime();
|
||||
TestSetTargetZeroTimeConstant();
|
||||
TestExponentialInvalidPreviousZeroValue();
|
||||
TestSettingValueCurveTwice();
|
||||
|
||||
return gFailCount > 0;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ tags=capturestream
|
|||
[test_audioParamGain.html]
|
||||
[test_audioParamLinearRamp.html]
|
||||
[test_audioParamSetCurveAtTime.html]
|
||||
[test_audioParamSetCurveAtTimeTwice.html]
|
||||
[test_audioParamSetCurveAtTimeZeroDuration.html]
|
||||
[test_audioParamSetTargetAtTime.html]
|
||||
[test_audioParamSetValueAtTime.html]
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var curve2 = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
curve2[i] = Math.sin(220 * 6 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(curve2, T0, this.duration/2);
|
||||
//Set a diffrent curve from the first one
|
||||
gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.duration = 1024 / context.sampleRate;
|
||||
this.curve = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)))];
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -248,19 +248,14 @@ WebMReader::AsyncReadMetadata()
|
|||
nsresult
|
||||
WebMReader::RetrieveWebMMetadata(MediaInfo* aInfo)
|
||||
{
|
||||
// We can't use OnTaskQueue() here because of the wacky initialization task
|
||||
// queue that TrackBuffer uses. We should be able to fix this when we do
|
||||
// bug 1148234.
|
||||
MOZ_ASSERT(mDecoder->OnDecodeTaskQueue());
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
nestegg_io io;
|
||||
io.read = webm_read;
|
||||
io.seek = webm_seek;
|
||||
io.tell = webm_tell;
|
||||
io.userdata = &mResource;
|
||||
int64_t maxOffset = mDecoder->HasInitializationData() ?
|
||||
mBufferedState->GetInitEndOffset() : -1;
|
||||
int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
|
||||
int r = nestegg_init(&mContext, io, &webm_log, -1);
|
||||
if (r == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,9 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
|
||||
const gIsOSX = ("nsILocalFileMac" in Ci);
|
||||
const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
|
||||
("@mozilla.org/gio-service;1" in Cc);
|
||||
const gIsWindows = mozinfo.os == "win";
|
||||
const gIsOSX = mozinfo.os == "mac";
|
||||
const gIsLinux = mozinfo.os == "linux";
|
||||
const gDirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
|
||||
// Finds the test plugin library
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Plugin registry uses different field delimeters on different platforms
|
||||
var DELIM = ":";
|
||||
if ("@mozilla.org/windows-registry-key;1" in Components.classes)
|
||||
DELIM = "|";
|
||||
var DELIM = mozinfo.os == "win" ? "|" : ":";
|
||||
|
||||
var gProfD = do_get_profile_startup();
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Plugin registry uses different field delimeters on different platforms
|
||||
var DELIM = ":";
|
||||
if ("@mozilla.org/windows-registry-key;1" in Components.classes)
|
||||
DELIM = "|";
|
||||
var DELIM = mozinfo.os == "win" ? "|" : ":";
|
||||
|
||||
var gProfD = do_get_profile_startup();
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Plugin registry uses different field delimeters on different platforms
|
||||
var DELIM = ":";
|
||||
if ("@mozilla.org/windows-registry-key;1" in Components.classes)
|
||||
DELIM = "|";
|
||||
var DELIM = mozinfo.os == "win" ? "|" : ":";
|
||||
|
||||
var gProfD = do_get_profile_startup();
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationCallbacks.h"
|
||||
#include "PresentationRequest.h"
|
||||
#include "PresentationSession.h"
|
||||
#include "PresentationConnection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -47,18 +47,18 @@ PresentationRequesterCallback::NotifySuccess()
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// At the sender side, this function must get called after the transport
|
||||
// channel is ready. So we simply set the session state as connected.
|
||||
nsRefPtr<PresentationSession> session =
|
||||
PresentationSession::Create(mRequest->GetOwner(), mSessionId,
|
||||
PresentationSessionState::Connected);
|
||||
if (NS_WARN_IF(!session)) {
|
||||
// channel is ready. So we simply set the connection state as connected.
|
||||
nsRefPtr<PresentationConnection> connection =
|
||||
PresentationConnection::Create(mRequest->GetOwner(), mSessionId,
|
||||
PresentationConnectionState::Connected);
|
||||
if (NS_WARN_IF(!connection)) {
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(session);
|
||||
mPromise->MaybeResolve(connection);
|
||||
|
||||
return mRequest->DispatchSessionConnectEvent(session);
|
||||
return mRequest->DispatchConnectionAvailableEvent(connection);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -10,52 +10,52 @@
|
|||
#include "nsIPresentationService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "PresentationSession.h"
|
||||
#include "PresentationConnection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationSession)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationConnection)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(PresentationSession, DOMEventTargetHelper)
|
||||
NS_IMPL_ADDREF_INHERITED(PresentationConnection, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationSession)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
PresentationSession::PresentationSession(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationSessionState aState)
|
||||
PresentationConnection::PresentationConnection(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationConnectionState aState)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mId(aId)
|
||||
, mState(aState)
|
||||
{
|
||||
}
|
||||
|
||||
/* virtual */ PresentationSession::~PresentationSession()
|
||||
/* virtual */ PresentationConnection::~PresentationConnection()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<PresentationSession>
|
||||
PresentationSession::Create(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationSessionState aState)
|
||||
/* static */ already_AddRefed<PresentationConnection>
|
||||
PresentationConnection::Create(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationConnectionState aState)
|
||||
{
|
||||
nsRefPtr<PresentationSession> session =
|
||||
new PresentationSession(aWindow, aId, aState);
|
||||
return NS_WARN_IF(!session->Init()) ? nullptr : session.forget();
|
||||
nsRefPtr<PresentationConnection> connection =
|
||||
new PresentationConnection(aWindow, aId, aState);
|
||||
return NS_WARN_IF(!connection->Init()) ? nullptr : connection.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
PresentationSession::Init()
|
||||
PresentationConnection::Init()
|
||||
{
|
||||
if (NS_WARN_IF(mId.IsEmpty())) {
|
||||
return false;
|
||||
|
@ -76,7 +76,7 @@ PresentationSession::Init()
|
|||
}
|
||||
|
||||
void
|
||||
PresentationSession::Shutdown()
|
||||
PresentationConnection::Shutdown()
|
||||
{
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
|
@ -89,37 +89,37 @@ PresentationSession::Shutdown()
|
|||
}
|
||||
|
||||
/* virtual */ void
|
||||
PresentationSession::DisconnectFromOwner()
|
||||
PresentationConnection::DisconnectFromOwner()
|
||||
{
|
||||
Shutdown();
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
PresentationSession::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
PresentationConnection::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return PresentationSessionBinding::Wrap(aCx, this, aGivenProto);
|
||||
return PresentationConnectionBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PresentationSession::GetId(nsAString& aId) const
|
||||
PresentationConnection::GetId(nsAString& aId) const
|
||||
{
|
||||
aId = mId;
|
||||
}
|
||||
|
||||
PresentationSessionState
|
||||
PresentationSession::State() const
|
||||
PresentationConnectionState
|
||||
PresentationConnection::State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void
|
||||
PresentationSession::Send(const nsAString& aData,
|
||||
PresentationConnection::Send(const nsAString& aData,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Sending is not allowed if the session is not connected.
|
||||
if (NS_WARN_IF(mState != PresentationSessionState::Connected)) {
|
||||
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
@ -153,10 +153,10 @@ PresentationSession::Send(const nsAString& aData,
|
|||
}
|
||||
|
||||
void
|
||||
PresentationSession::Close(ErrorResult& aRv)
|
||||
PresentationConnection::Close(ErrorResult& aRv)
|
||||
{
|
||||
// It only works when the state is CONNECTED.
|
||||
if (NS_WARN_IF(mState != PresentationSessionState::Connected)) {
|
||||
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,10 +165,10 @@ PresentationSession::Close(ErrorResult& aRv)
|
|||
}
|
||||
|
||||
void
|
||||
PresentationSession::Terminate(ErrorResult& aRv)
|
||||
PresentationConnection::Terminate(ErrorResult& aRv)
|
||||
{
|
||||
// It only works when the state is CONNECTED.
|
||||
if (NS_WARN_IF(mState != PresentationSessionState::Connected)) {
|
||||
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -183,23 +183,23 @@ PresentationSession::Terminate(ErrorResult& aRv)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSession::NotifyStateChange(const nsAString& aSessionId,
|
||||
PresentationConnection::NotifyStateChange(const nsAString& aSessionId,
|
||||
uint16_t aState)
|
||||
{
|
||||
if (!aSessionId.Equals(mId)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
PresentationSessionState state;
|
||||
PresentationConnectionState state;
|
||||
switch (aState) {
|
||||
case nsIPresentationSessionListener::STATE_CONNECTED:
|
||||
state = PresentationSessionState::Connected;
|
||||
state = PresentationConnectionState::Connected;
|
||||
break;
|
||||
case nsIPresentationSessionListener::STATE_CLOSED:
|
||||
state = PresentationSessionState::Closed;
|
||||
state = PresentationConnectionState::Closed;
|
||||
break;
|
||||
case nsIPresentationSessionListener::STATE_TERMINATED:
|
||||
state = PresentationSessionState::Terminated;
|
||||
state = PresentationConnectionState::Terminated;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unknown presentation session state.");
|
||||
|
@ -213,7 +213,7 @@ PresentationSession::NotifyStateChange(const nsAString& aSessionId,
|
|||
mState = state;
|
||||
|
||||
// Unregister session listener if the session is no longer connected.
|
||||
if (mState == PresentationSessionState::Terminated) {
|
||||
if (mState == PresentationConnectionState::Terminated) {
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!service)) {
|
||||
|
@ -230,7 +230,7 @@ PresentationSession::NotifyStateChange(const nsAString& aSessionId,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PresentationSession::NotifyMessage(const nsAString& aSessionId,
|
||||
PresentationConnection::NotifyMessage(const nsAString& aSessionId,
|
||||
const nsACString& aData)
|
||||
{
|
||||
if (!aSessionId.Equals(mId)) {
|
||||
|
@ -238,7 +238,7 @@ PresentationSession::NotifyMessage(const nsAString& aSessionId,
|
|||
}
|
||||
|
||||
// No message should be expected when the session is not connected.
|
||||
if (NS_WARN_IF(mState != PresentationSessionState::Connected)) {
|
||||
if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
|
@ -258,7 +258,7 @@ PresentationSession::NotifyMessage(const nsAString& aSessionId,
|
|||
}
|
||||
|
||||
nsresult
|
||||
PresentationSession::DispatchStateChangeEvent()
|
||||
PresentationConnection::DispatchStateChangeEvent()
|
||||
{
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("statechange"), false);
|
||||
|
@ -266,7 +266,7 @@ PresentationSession::DispatchStateChangeEvent()
|
|||
}
|
||||
|
||||
nsresult
|
||||
PresentationSession::DispatchMessageEvent(JS::Handle<JS::Value> aData)
|
||||
PresentationConnection::DispatchMessageEvent(JS::Handle<JS::Value> aData)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
|
@ -4,28 +4,28 @@
|
|||
* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_PresentationSession_h
|
||||
#define mozilla_dom_PresentationSession_h
|
||||
#ifndef mozilla_dom_PresentationConnection_h
|
||||
#define mozilla_dom_PresentationConnection_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/PresentationSessionBinding.h"
|
||||
#include "mozilla/dom/PresentationConnectionBinding.h"
|
||||
#include "nsIPresentationListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PresentationSession final : public DOMEventTargetHelper
|
||||
, public nsIPresentationSessionListener
|
||||
class PresentationConnection final : public DOMEventTargetHelper
|
||||
, public nsIPresentationSessionListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationSession,
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection,
|
||||
DOMEventTargetHelper)
|
||||
NS_DECL_NSIPRESENTATIONSESSIONLISTENER
|
||||
|
||||
static already_AddRefed<PresentationSession> Create(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationSessionState aState);
|
||||
static already_AddRefed<PresentationConnection> Create(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationConnectionState aState);
|
||||
|
||||
virtual void DisconnectFromOwner() override;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public:
|
|||
// WebIDL (public APIs)
|
||||
void GetId(nsAString& aId) const;
|
||||
|
||||
PresentationSessionState State() const;
|
||||
PresentationConnectionState State() const;
|
||||
|
||||
void Send(const nsAString& aData,
|
||||
ErrorResult& aRv);
|
||||
|
@ -48,11 +48,11 @@ public:
|
|||
IMPL_EVENT_HANDLER(message);
|
||||
|
||||
private:
|
||||
PresentationSession(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationSessionState aState);
|
||||
PresentationConnection(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aId,
|
||||
PresentationConnectionState aState);
|
||||
|
||||
~PresentationSession();
|
||||
~PresentationConnection();
|
||||
|
||||
bool Init();
|
||||
|
||||
|
@ -63,10 +63,10 @@ private:
|
|||
nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData);
|
||||
|
||||
nsString mId;
|
||||
PresentationSessionState mState;
|
||||
PresentationConnectionState mState;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PresentationSession_h
|
||||
#endif // mozilla_dom_PresentationConnection_h
|
|
@ -11,7 +11,7 @@
|
|||
#include "nsIPresentationService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PresentationReceiver.h"
|
||||
#include "PresentationSession.h"
|
||||
#include "PresentationConnection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -19,14 +19,14 @@ using namespace mozilla::dom;
|
|||
NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationReceiver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationReceiver, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessions)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingGetSessionPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnections)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingGetConnectionPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationReceiver, DOMEventTargetHelper)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessions)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingGetSessionPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConnections)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingGetConnectionPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PresentationReceiver, DOMEventTargetHelper)
|
||||
|
@ -86,8 +86,8 @@ PresentationReceiver::Init(const nsAString& aSessionId)
|
|||
|
||||
void PresentationReceiver::Shutdown()
|
||||
{
|
||||
mSessions.Clear();
|
||||
mPendingGetSessionPromises.Clear();
|
||||
mConnections.Clear();
|
||||
mPendingGetConnectionPromises.Clear();
|
||||
|
||||
// Unregister listener for incoming sessions.
|
||||
nsCOMPtr<nsIPresentationService> service =
|
||||
|
@ -115,7 +115,7 @@ PresentationReceiver::WrapObject(JSContext* aCx,
|
|||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
PresentationReceiver::GetSession(ErrorResult& aRv)
|
||||
PresentationReceiver::GetConnection(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
||||
|
@ -128,20 +128,20 @@ PresentationReceiver::GetSession(ErrorResult& aRv)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// If there's no existing session, leave the promise pending until a
|
||||
// If there's no existing connection, leave the promise pending until a
|
||||
// connecting request arrives from the controlling browsing context (sender).
|
||||
// http://w3c.github.io/presentation-api/#dom-presentation-getsession
|
||||
if (!mSessions.IsEmpty()) {
|
||||
promise->MaybeResolve(mSessions[0]);
|
||||
// http://w3c.github.io/presentation-api/#dom-presentation-getconnection
|
||||
if (!mConnections.IsEmpty()) {
|
||||
promise->MaybeResolve(mConnections[0]);
|
||||
} else {
|
||||
mPendingGetSessionPromises.AppendElement(promise);
|
||||
mPendingGetConnectionPromises.AppendElement(promise);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
PresentationReceiver::GetSessions(ErrorResult& aRv) const
|
||||
PresentationReceiver::GetConnections(ErrorResult& aRv) const
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
if (NS_WARN_IF(!global)) {
|
||||
|
@ -154,7 +154,7 @@ PresentationReceiver::GetSessions(ErrorResult& aRv) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
promise->MaybeResolve(mSessions);
|
||||
promise->MaybeResolve(mConnections);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
@ -166,29 +166,29 @@ PresentationReceiver::NotifySessionConnect(uint64_t aWindowId,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsRefPtr<PresentationSession> session =
|
||||
PresentationSession::Create(GetOwner(), aSessionId,
|
||||
PresentationSessionState::Closed);
|
||||
if (NS_WARN_IF(!session)) {
|
||||
nsRefPtr<PresentationConnection> connection =
|
||||
PresentationConnection::Create(GetOwner(), aSessionId,
|
||||
PresentationConnectionState::Closed);
|
||||
if (NS_WARN_IF(!connection)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
mSessions.AppendElement(session);
|
||||
mConnections.AppendElement(connection);
|
||||
|
||||
// Resolve pending |GetSession| promises if any.
|
||||
if (!mPendingGetSessionPromises.IsEmpty()) {
|
||||
for(uint32_t i = 0; i < mPendingGetSessionPromises.Length(); i++) {
|
||||
mPendingGetSessionPromises[i]->MaybeResolve(session);
|
||||
// Resolve pending |GetConnection| promises if any.
|
||||
if (!mPendingGetConnectionPromises.IsEmpty()) {
|
||||
for(uint32_t i = 0; i < mPendingGetConnectionPromises.Length(); i++) {
|
||||
mPendingGetConnectionPromises[i]->MaybeResolve(connection);
|
||||
}
|
||||
mPendingGetSessionPromises.Clear();
|
||||
mPendingGetConnectionPromises.Clear();
|
||||
}
|
||||
|
||||
return DispatchSessionAvailableEvent();
|
||||
return DispatchConnectionAvailableEvent();
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresentationReceiver::DispatchSessionAvailableEvent()
|
||||
PresentationReceiver::DispatchConnectionAvailableEvent()
|
||||
{
|
||||
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("sessionavailable"), false);
|
||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("connectionavailable"), false);
|
||||
return asyncDispatcher->PostDOMEvent();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class PresentationSession;
|
||||
class PresentationConnection;
|
||||
|
||||
class PresentationReceiver final : public DOMEventTargetHelper
|
||||
, public nsIPresentationRespondingListener
|
||||
|
@ -34,11 +34,11 @@ public:
|
|||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// WebIDL (public APIs)
|
||||
already_AddRefed<Promise> GetSession(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> GetConnection(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> GetSessions(ErrorResult& aRv) const;
|
||||
already_AddRefed<Promise> GetConnections(ErrorResult& aRv) const;
|
||||
|
||||
IMPL_EVENT_HANDLER(sessionavailable);
|
||||
IMPL_EVENT_HANDLER(connectionavailable);
|
||||
|
||||
private:
|
||||
explicit PresentationReceiver(nsPIDOMWindow* aWindow);
|
||||
|
@ -49,14 +49,14 @@ private:
|
|||
|
||||
void Shutdown();
|
||||
|
||||
nsresult DispatchSessionAvailableEvent();
|
||||
nsresult DispatchConnectionAvailableEvent();
|
||||
|
||||
// Store the inner window ID for |UnregisterRespondingListener| call in
|
||||
// |Shutdown| since the inner window may not exist at that moment.
|
||||
uint64_t mWindowId;
|
||||
|
||||
nsTArray<nsRefPtr<PresentationSession>> mSessions;
|
||||
nsTArray<nsRefPtr<Promise>> mPendingGetSessionPromises;
|
||||
nsTArray<nsRefPtr<PresentationConnection>> mConnections;
|
||||
nsTArray<nsRefPtr<Promise>> mPendingGetConnectionPromises;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/PresentationRequestBinding.h"
|
||||
#include "mozilla/dom/PresentationSessionConnectEvent.h"
|
||||
#include "mozilla/dom/PresentationConnectionAvailableEvent.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozIThirdPartyUtil.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
@ -151,15 +151,15 @@ PresentationRequest::GetAvailability(ErrorResult& aRv)
|
|||
}
|
||||
|
||||
nsresult
|
||||
PresentationRequest::DispatchSessionConnectEvent(PresentationSession* aSession)
|
||||
PresentationRequest::DispatchConnectionAvailableEvent(PresentationConnection* aConnection)
|
||||
{
|
||||
PresentationSessionConnectEventInit init;
|
||||
init.mSession = aSession;
|
||||
PresentationConnectionAvailableEventInit init;
|
||||
init.mConnection = aConnection;
|
||||
|
||||
nsRefPtr<PresentationSessionConnectEvent> event =
|
||||
PresentationSessionConnectEvent::Constructor(this,
|
||||
NS_LITERAL_STRING("sessionconnect"),
|
||||
init);
|
||||
nsRefPtr<PresentationConnectionAvailableEvent> event =
|
||||
PresentationConnectionAvailableEvent::Constructor(this,
|
||||
NS_LITERAL_STRING("connectionavailable"),
|
||||
init);
|
||||
if (NS_WARN_IF(!event)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace dom {
|
|||
|
||||
class Promise;
|
||||
class PresentationAvailability;
|
||||
class PresentationSession;
|
||||
class PresentationConnection;
|
||||
|
||||
class PresentationRequest final : public DOMEventTargetHelper
|
||||
{
|
||||
|
@ -35,9 +35,9 @@ public:
|
|||
|
||||
already_AddRefed<Promise> GetAvailability(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(sessionconnect);
|
||||
IMPL_EVENT_HANDLER(connectionavailable);
|
||||
|
||||
nsresult DispatchSessionConnectEvent(PresentationSession* aSession);
|
||||
nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection);
|
||||
|
||||
private:
|
||||
PresentationRequest(nsPIDOMWindow* aWindow,
|
||||
|
|
|
@ -16,11 +16,11 @@ EXPORTS.mozilla.dom += [
|
|||
'Presentation.h',
|
||||
'PresentationAvailability.h',
|
||||
'PresentationCallbacks.h',
|
||||
'PresentationConnection.h',
|
||||
'PresentationDeviceManager.h',
|
||||
'PresentationReceiver.h',
|
||||
'PresentationRequest.h',
|
||||
'PresentationService.h',
|
||||
'PresentationSession.h',
|
||||
'PresentationSessionInfo.h',
|
||||
'PresentationSessionTransport.h',
|
||||
]
|
||||
|
@ -32,11 +32,11 @@ UNIFIED_SOURCES += [
|
|||
'Presentation.cpp',
|
||||
'PresentationAvailability.cpp',
|
||||
'PresentationCallbacks.cpp',
|
||||
'PresentationConnection.cpp',
|
||||
'PresentationDeviceManager.cpp',
|
||||
'PresentationReceiver.cpp',
|
||||
'PresentationRequest.cpp',
|
||||
'PresentationService.cpp',
|
||||
'PresentationSession.cpp',
|
||||
'PresentationSessionInfo.cpp',
|
||||
'PresentationSessionRequest.cpp',
|
||||
'PresentationSessionTransport.cpp',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API on a non-receiver page at receiver side (OOP)</title>
|
||||
<title>Test for B2G PresentationReceiver on a non-receiver page at receiver side (OOP)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
|
@ -26,7 +26,7 @@ function finish() {
|
|||
alert('DONE');
|
||||
}
|
||||
|
||||
function testSessionAvailable() {
|
||||
function testConnectionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available in OOP pages.");
|
||||
ok(!navigator.presentation.receiver, "Non-receiving OOP pages shouldn't get a presentation receiver instance.");
|
||||
|
@ -34,7 +34,7 @@ function testSessionAvailable() {
|
|||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
testConnectionAvailable().
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API at receiver side</title>
|
||||
<title>Test for B2G PresentationReceiver at receiver side</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
|
@ -30,23 +30,23 @@ function finish() {
|
|||
window.parent.postMessage('DONE', '*');
|
||||
}
|
||||
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSessionAvailable() {
|
||||
function testConnectionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available.");
|
||||
|
||||
navigator.presentation.receiver.getSession().then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
navigator.presentation.receiver.getConnection().then(
|
||||
function(aConnection) {
|
||||
connection = aConnection;
|
||||
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "closed", "Session state at receiver side should be closed by default.");
|
||||
ok(connection.id, "Connection ID should be set: " + connection.id);
|
||||
is(connection.state, "closed", "Connection state at receiver side should be closed by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting the session: " + aError);
|
||||
ok(false, "Error occurred when getting the connection: " + aError);
|
||||
finish();
|
||||
aReject();
|
||||
}
|
||||
|
@ -54,11 +54,11 @@ function testSessionAvailable() {
|
|||
});
|
||||
}
|
||||
|
||||
function testSessionReady() {
|
||||
function testConnectionReady() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "connected", "Session state should become connected.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "connected", "Connection state should become connected.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
|
@ -70,8 +70,8 @@ function testIncomingMessage() {
|
|||
return new Promise(function(aResolve, aReject) {
|
||||
const incomingMessage = "test incoming message";
|
||||
|
||||
session.addEventListener('message', function messageHandler(aEvent) {
|
||||
session.removeEventListener('message', messageHandler);
|
||||
connection.addEventListener('message', function messageHandler(aEvent) {
|
||||
connection.removeEventListener('message', messageHandler);
|
||||
is(aEvent.data, incomingMessage, "An incoming message should be received.");
|
||||
aResolve();
|
||||
});
|
||||
|
@ -81,22 +81,22 @@ function testIncomingMessage() {
|
|||
});
|
||||
}
|
||||
|
||||
function testTerminateSession() {
|
||||
function testTerminateConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "terminated", "Connection should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.terminate();
|
||||
connection.terminate();
|
||||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
then(testSessionReady).
|
||||
testConnectionAvailable().
|
||||
then(testConnectionReady).
|
||||
then(testIncomingMessage).
|
||||
then(testTerminateSession).
|
||||
then(testTerminateConnection).
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for startSession errors of B2G Presentation API at receiver side</title>
|
||||
<title>Test for connection establishing errors of B2G Presentation API at receiver side</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
|
@ -30,23 +30,23 @@ function finish() {
|
|||
window.parent.postMessage('DONE', '*');
|
||||
}
|
||||
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSessionAvailable() {
|
||||
function testConnectionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available.");
|
||||
|
||||
navigator.presentation.receiver.getSession().then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
navigator.presentation.receiver.getConnection().then(
|
||||
function(aConnection) {
|
||||
connection = aConnection;
|
||||
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "closed", "Session state at receiver side should be closed by default.");
|
||||
ok(connection.id, "Connection ID should be set: " + connection.id);
|
||||
is(connection.state, "closed", "Connection state at receiver side should be closed by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting the session: " + aError);
|
||||
ok(false, "Error occurred when getting the connection: " + aError);
|
||||
finish();
|
||||
aReject();
|
||||
}
|
||||
|
@ -56,9 +56,9 @@ function testSessionAvailable() {
|
|||
|
||||
function testUnexpectedControlChannelClose() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session state should become terminated.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "terminated", "Connection state should become terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
|
@ -67,7 +67,7 @@ function testUnexpectedControlChannelClose() {
|
|||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
testConnectionAvailable().
|
||||
then(testUnexpectedControlChannelClose).
|
||||
then(finish);
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API at receiver side (OOP)</title>
|
||||
<title>Test for B2G PresentationReceiver at receiver side (OOP)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
|
@ -30,23 +30,23 @@ function finish() {
|
|||
alert('DONE');
|
||||
}
|
||||
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSessionAvailable() {
|
||||
function testConnectionAvailable() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available.");
|
||||
|
||||
navigator.presentation.receiver.getSession().then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
navigator.presentation.receiver.getConnection().then(
|
||||
function(aConnection) {
|
||||
connection = aConnection;
|
||||
|
||||
ok(session.id, "Session ID should be set: " + session.id);
|
||||
is(session.state, "closed", "Session state at receiver side should be closed by default.");
|
||||
ok(connection.id, "Connection ID should be set: " + connection.id);
|
||||
is(connection.state, "closed", "Connection state at receiver side should be closed by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when getting the session: " + aError);
|
||||
ok(false, "Error occurred when getting the connection: " + aError);
|
||||
finish();
|
||||
aReject();
|
||||
}
|
||||
|
@ -54,11 +54,11 @@ function testSessionAvailable() {
|
|||
});
|
||||
}
|
||||
|
||||
function testSessionReady() {
|
||||
function testConnectionReady() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "connected", "Session state should become connected.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "connected", "Connection state should become connected.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
|
@ -70,8 +70,8 @@ function testIncomingMessage() {
|
|||
return new Promise(function(aResolve, aReject) {
|
||||
const incomingMessage = "test incoming message";
|
||||
|
||||
session.addEventListener('message', function messageHandler(aEvent) {
|
||||
session.removeEventListener('message', messageHandler);
|
||||
connection.addEventListener('message', function messageHandler(aEvent) {
|
||||
connection.removeEventListener('message', messageHandler);
|
||||
is(aEvent.data, incomingMessage, "An incoming message should be received.");
|
||||
aResolve();
|
||||
});
|
||||
|
@ -81,22 +81,22 @@ function testIncomingMessage() {
|
|||
});
|
||||
}
|
||||
|
||||
function testTerminateSession() {
|
||||
function testTerminateConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "terminated", "Connection should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.terminate();
|
||||
connection.terminate();
|
||||
});
|
||||
}
|
||||
|
||||
testSessionAvailable().
|
||||
then(testSessionReady).
|
||||
testConnectionAvailable().
|
||||
then(testConnectionReady).
|
||||
then(testIncomingMessage).
|
||||
then(testTerminateSession).
|
||||
then(testTerminateConnection).
|
||||
then(finish);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -5,21 +5,21 @@ support-files =
|
|||
file_presentation_receiver.html
|
||||
file_presentation_receiver_oop.html
|
||||
file_presentation_non_receiver_oop.html
|
||||
file_presentation_receiver_start_session_error.html
|
||||
file_presentation_receiver_establish_connection_error.html
|
||||
|
||||
[test_presentation_device_info.html]
|
||||
[test_presentation_device_info_permission.html]
|
||||
[test_presentation_sender_disconnect.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_sender_start_session_error.html]
|
||||
[test_presentation_sender_establish_connection_error.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_sender.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_sender_default_request.html]
|
||||
skip-if = toolkit == 'android' # Bug 1129785
|
||||
[test_presentation_receiver_start_session_error.html]
|
||||
[test_presentation_receiver_establish_connection_error.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android' || os == 'mac' || os == 'win' || buildapp == 'mulet') # Bug 1129785, Bug 1204709
|
||||
[test_presentation_receiver_start_session_timeout.html]
|
||||
[test_presentation_receiver_establish_connection_timeout.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
[test_presentation_receiver.html]
|
||||
skip-if = (e10s || toolkit == 'gonk' || toolkit == 'android') # Bug 1129785
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API at receiver side</title>
|
||||
<title>Test for B2G PresentationConnection API at receiver side</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G Presentation Session API at receiver side</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G PresentationConnection API at receiver side</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
|
|
@ -4,22 +4,21 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for startSession errors of B2G Presentation API at receiver side</title>
|
||||
<title>Test for connection establishing errors of B2G Presentation API at receiver side</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for startSession errors of B2G Presentation API at receiver side</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at receiver side</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
||||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
|
||||
var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver_start_session_error.html');
|
||||
var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver_establish_connection_error.html');
|
||||
|
||||
var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIObserverService);
|
||||
var session;
|
||||
|
||||
function setup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
|
@ -4,12 +4,12 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for startSession timeout of B2G Presentation API at receiver side</title>
|
||||
<title>Test for connection establishing timeout of B2G Presentation API at receiver side</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for startSession timeout of B2G Presentation API at receiver side</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing timeout of B2G Presentation API at receiver side</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
|
@ -4,12 +4,12 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for B2G Presentation Session API at receiver side (OOP)</title>
|
||||
<title>Test for B2G PresentationConnection API at receiver side (OOP)</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test B2G Presentation Session API at receiver side (OOP)</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test B2G PresentationConnection API at receiver side (OOP)</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
|
||||
var request;
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
|
@ -41,7 +41,7 @@ function testSetup() {
|
|||
});
|
||||
}
|
||||
|
||||
function testStartSession() {
|
||||
function testStartConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -87,32 +87,32 @@ function testStartSession() {
|
|||
info("Data notification is enabled for data transport channel.");
|
||||
});
|
||||
|
||||
var sessionFromEvent;
|
||||
request.onsessionconnect = function(aEvent) {
|
||||
request.onsessionconnect = null;
|
||||
sessionFromEvent = aEvent.session;
|
||||
ok(sessionFromEvent, "|sessionconnect| event is fired with a session.");
|
||||
var connectionFromEvent;
|
||||
request.onconnectionavailable = function(aEvent) {
|
||||
request.onconnectionavailable = null;
|
||||
connectionFromEvent = aEvent.connection;
|
||||
ok(connectionFromEvent, "|connectionavailable| event is fired with a connection.");
|
||||
|
||||
if (session) {
|
||||
is(session, sessionFromEvent, "The session from promise and the one from |sessionconnect| event should be the same.");
|
||||
if (connection) {
|
||||
is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
|
||||
aResolve();
|
||||
}
|
||||
};
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
ok(session, "Session should be availlable.");
|
||||
ok(session.id, "Session ID should be set.");
|
||||
is(session.state, "connected", "Session state at sender side should be connected by default.");
|
||||
function(aConnection) {
|
||||
connection = aConnection;
|
||||
ok(connection, "Connection should be available.");
|
||||
ok(connection.id, "Connection ID should be set.");
|
||||
is(connection.state, "connected", "Connection state at sender side should be connected by default.");
|
||||
|
||||
if (sessionFromEvent) {
|
||||
is(session, sessionFromEvent, "The session from promise and the one from |sessionconnect| event should be the same.");
|
||||
if (connectionFromEvent) {
|
||||
is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same.");
|
||||
aResolve();
|
||||
}
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when starting session: " + aError);
|
||||
ok(false, "Error occurred when establishing a connection: " + aError);
|
||||
teardown();
|
||||
aReject();
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ function testSend() {
|
|||
aResolve();
|
||||
});
|
||||
|
||||
session.send(outgoingMessage);
|
||||
connection.send(outgoingMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -138,8 +138,8 @@ function testIncomingMessage() {
|
|||
return new Promise(function(aResolve, aReject) {
|
||||
const incomingMessage = "test incoming message";
|
||||
|
||||
session.addEventListener('message', function messageHandler(aEvent) {
|
||||
session.removeEventListener('message', messageHandler);
|
||||
connection.addEventListener('message', function messageHandler(aEvent) {
|
||||
connection.removeEventListener('message', messageHandler);
|
||||
is(aEvent.data, incomingMessage, "An incoming message should be received.");
|
||||
aResolve();
|
||||
});
|
||||
|
@ -148,20 +148,20 @@ function testIncomingMessage() {
|
|||
});
|
||||
}
|
||||
|
||||
function testTerminateSession() {
|
||||
function testTerminateConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
|
||||
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
|
||||
info("The data transport is closed. " + aReason);
|
||||
});
|
||||
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "terminated", "Connection should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.terminate();
|
||||
connection.terminate();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -179,10 +179,10 @@ function runTests() {
|
|||
ok(window.PresentationRequest, "PresentationRequest should be available.");
|
||||
|
||||
testSetup().
|
||||
then(testStartSession).
|
||||
then(testStartConnection).
|
||||
then(testSend).
|
||||
then(testIncomingMessage).
|
||||
then(testTerminateSession).
|
||||
then(testTerminateConnection).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
'use strict';
|
||||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
|
@ -40,7 +40,7 @@ function testSetup() {
|
|||
});
|
||||
}
|
||||
|
||||
function testStartSession() {
|
||||
function testStartConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -83,12 +83,12 @@ function testStartSession() {
|
|||
|
||||
ok(!navigator.presentation.receiver, "Sender shouldn't get a presentation receiver instance.");
|
||||
|
||||
navigator.presentation.defaultRequest.onsessionconnect = function(aEvent) {
|
||||
navigator.presentation.defaultRequest.onsessionconnect = null;
|
||||
session = aEvent.session;
|
||||
ok(session, "|sessionconnect| event is fired with a session.");
|
||||
ok(session.id, "Session ID should be set.");
|
||||
is(session.state, "connected", "Session state at sender side should be connected by default.");
|
||||
navigator.presentation.defaultRequest.onconnectionavailable = function(aEvent) {
|
||||
navigator.presentation.defaultRequest.onconnectionavailable = null;
|
||||
connection = aEvent.connection;
|
||||
ok(connection, "|connectionavailable| event is fired with a connection.");
|
||||
ok(connection.id, "Connection ID should be set.");
|
||||
is(connection.state, "connected", "Connection state at sender side should be connected by default.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
|
@ -97,20 +97,20 @@ function testStartSession() {
|
|||
});
|
||||
}
|
||||
|
||||
function testTerminateSession() {
|
||||
function testTerminateConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
|
||||
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
|
||||
info("The data transport is closed. " + aReason);
|
||||
});
|
||||
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "terminated", "Session should be terminated.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "terminated", "Connection should be terminated.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
session.terminate();
|
||||
connection.terminate();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -129,8 +129,8 @@ function runTests() {
|
|||
ok(navigator.presentation, "navigator.presentation should be available.");
|
||||
|
||||
testSetup().
|
||||
then(testStartSession).
|
||||
then(testTerminateSession).
|
||||
then(testStartConnection).
|
||||
then(testTerminateConnection).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for session disconnection of B2G Presentation API at sender side</title>
|
||||
<title>Test for disconnection of B2G Presentation API at sender side</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for session disconnection of B2G Presentation API at sender side</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for disconnection of B2G Presentation API at sender side</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
||||
|
||||
var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js'));
|
||||
var request;
|
||||
var session;
|
||||
var connection;
|
||||
|
||||
function testSetup() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
|
@ -41,7 +41,7 @@ function testSetup() {
|
|||
});
|
||||
}
|
||||
|
||||
function testStartSession() {
|
||||
function testStartConnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -88,15 +88,15 @@ function testStartSession() {
|
|||
});
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
session = aSession;
|
||||
ok(session, "Session should be availlable.");
|
||||
ok(session.id, "Session ID should be set.");
|
||||
is(session.state, "connected", "Session state at sender side should be connected by default.");
|
||||
function(aConnection) {
|
||||
connection = aConnection;
|
||||
ok(connection, "Connection should be available.");
|
||||
ok(connection.id, "Connection ID should be set.");
|
||||
is(connection.state, "connected", "Connection state at sender side should be connected by default.");
|
||||
aResolve();
|
||||
},
|
||||
function(aError) {
|
||||
ok(false, "Error occurred when starting session: " + aError);
|
||||
ok(false, "Error occurred when establishing a connection: " + aError);
|
||||
teardown();
|
||||
aReject();
|
||||
}
|
||||
|
@ -104,16 +104,16 @@ function testStartSession() {
|
|||
});
|
||||
}
|
||||
|
||||
function testSessionDisconnection() {
|
||||
function testDisconnection() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
|
||||
gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
|
||||
info("The data transport is closed. " + aReason);
|
||||
});
|
||||
|
||||
session.onstatechange = function() {
|
||||
session.onstatechange = null;
|
||||
is(session.state, "closed", "Session should be closed.");
|
||||
connection.onstatechange = function() {
|
||||
connection.onstatechange = null;
|
||||
is(connection.state, "closed", "Connection should be closed.");
|
||||
aResolve();
|
||||
};
|
||||
|
||||
|
@ -135,8 +135,8 @@ function runTests() {
|
|||
ok(window.PresentationRequest, "PresentationRequest should be available.");
|
||||
|
||||
testSetup().
|
||||
then(testStartSession).
|
||||
then(testSessionDisconnection).
|
||||
then(testStartConnection).
|
||||
then(testDisconnection).
|
||||
then(teardown);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for startSession errors of B2G Presentation API at sender side</title>
|
||||
<title>Test for connection establishing errors of B2G Presentation API at sender side</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for startSession errors of B2G Presentation API at sender side</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at sender side</a>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
'use strict';
|
||||
|
@ -51,7 +51,7 @@ function testCreateRequestWithEmptyURL() {
|
|||
});
|
||||
}
|
||||
|
||||
function testStartSessionCancelPrompt() {
|
||||
function testStartConnectionCancelPrompt() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -60,7 +60,7 @@ function testStartSessionCancelPrompt() {
|
|||
});
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
function(aConnection) {
|
||||
ok(false, "|start| shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ function testStartSessionCancelPrompt() {
|
|||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit() {
|
||||
function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -103,19 +103,19 @@ function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit()
|
|||
});
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
function(aConnection) {
|
||||
ok(false, "|start| shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady() {
|
||||
function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -157,19 +157,19 @@ function testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady()
|
|||
});
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
function(aConnection) {
|
||||
ok(false, "|start| shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function testStartSessionUnexpectedDataTransportClose() {
|
||||
function testStartConnectionUnexpectedDataTransportClose() {
|
||||
return new Promise(function(aResolve, aReject) {
|
||||
gScript.addMessageListener('device-prompt', function devicePromptHandler() {
|
||||
gScript.removeMessageListener('device-prompt', devicePromptHandler);
|
||||
|
@ -211,12 +211,12 @@ function testStartSessionUnexpectedDataTransportClose() {
|
|||
});
|
||||
|
||||
request.start().then(
|
||||
function(aSession) {
|
||||
function(aConnection) {
|
||||
ok(false, "|start| shouldn't succeed in this case.");
|
||||
aReject();
|
||||
},
|
||||
function(aError) {
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during starting session.");
|
||||
is(aError.name, "OperationError", "OperationError is expected when a connection error happens during establishing a connection.");
|
||||
aResolve();
|
||||
}
|
||||
);
|
||||
|
@ -238,10 +238,10 @@ function runTests() {
|
|||
|
||||
testCreateRequestWithEmptyURL().
|
||||
then(setup).
|
||||
then(testStartSessionCancelPrompt).
|
||||
then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportInit).
|
||||
then(testStartSessionUnexpectedControlChannelCloseBeforeDataTransportReady).
|
||||
then(testStartSessionUnexpectedDataTransportClose).
|
||||
then(testStartConnectionCancelPrompt).
|
||||
then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit).
|
||||
then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady).
|
||||
then(testStartConnectionUnexpectedDataTransportClose).
|
||||
then(teardown);
|
||||
}
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
interface Presentation : EventTarget {
|
||||
/*
|
||||
* This should be used by the UA as the default presentation request for the
|
||||
* controller. When the UA wishes to initiate a PresentationSession on the
|
||||
* controller's behalf, it MUST start a presentation session using the default
|
||||
* controller. When the UA wishes to initiate a PresentationConnection on the
|
||||
* controller's behalf, it MUST start a presentation connection using the default
|
||||
* presentation request (as if the controller had called |defaultRequest.start()|).
|
||||
*
|
||||
* Only used by controlling browsing context (senders).
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
enum PresentationSessionState
|
||||
enum PresentationConnectionState
|
||||
{
|
||||
// Existing presentation, and the communication channel is active.
|
||||
"connected",
|
||||
|
@ -19,9 +19,9 @@ enum PresentationSessionState
|
|||
|
||||
[Pref="dom.presentation.enabled",
|
||||
CheckAnyPermissions="presentation"]
|
||||
interface PresentationSession : EventTarget {
|
||||
interface PresentationConnection : EventTarget {
|
||||
/*
|
||||
* Unique id for all existing sessions.
|
||||
* Unique id for all existing connections.
|
||||
*/
|
||||
[Constant]
|
||||
readonly attribute DOMString id;
|
||||
|
@ -29,10 +29,10 @@ interface PresentationSession : EventTarget {
|
|||
/*
|
||||
* @value "connected", "closed", or "terminated".
|
||||
*/
|
||||
readonly attribute PresentationSessionState state;
|
||||
readonly attribute PresentationConnectionState state;
|
||||
|
||||
/*
|
||||
* It is called when session state changes.
|
||||
* It is called when connection state changes.
|
||||
*/
|
||||
attribute EventHandler onstatechange;
|
||||
|
||||
|
@ -55,8 +55,8 @@ interface PresentationSession : EventTarget {
|
|||
attribute EventHandler onmessage;
|
||||
|
||||
/*
|
||||
* Both the controlling and receving browsing context can close the session.
|
||||
* Then, the session state should turn into "closed".
|
||||
* Both the controlling and receiving browsing context can close the
|
||||
* connection. Then the connection state should turn into "closed".
|
||||
*
|
||||
* This function only works when the state is not "connected".
|
||||
*/
|
||||
|
@ -65,8 +65,8 @@ interface PresentationSession : EventTarget {
|
|||
// void close();
|
||||
|
||||
/*
|
||||
* Both the controlling and receving browsing context can terminate the session.
|
||||
* Then the session state should turn into "terminated".
|
||||
* Both the controlling and receiving browsing context can terminate the
|
||||
* connection. Then the connection state should turn into "terminated".
|
||||
*
|
||||
* This function only works when the state is not "connected".
|
||||
*/
|
|
@ -5,16 +5,16 @@
|
|||
*/
|
||||
|
||||
[Constructor(DOMString type,
|
||||
optional PresentationSessionConnectEventInit eventInitDict),
|
||||
optional PresentationConnectionAvailableEventInit eventInitDict),
|
||||
Pref="dom.presentation.enabled",
|
||||
CheckAnyPermissions="presentation"]
|
||||
interface PresentationSessionConnectEvent : Event
|
||||
interface PresentationConnectionAvailableEvent : Event
|
||||
{
|
||||
[SameObject]
|
||||
readonly attribute PresentationSession session;
|
||||
readonly attribute PresentationConnection connection;
|
||||
};
|
||||
|
||||
dictionary PresentationSessionConnectEventInit : EventInit
|
||||
dictionary PresentationConnectionAvailableEventInit : EventInit
|
||||
{
|
||||
required PresentationSession session;
|
||||
required PresentationConnection connection;
|
||||
};
|
|
@ -8,20 +8,20 @@
|
|||
CheckAnyPermissions="presentation"]
|
||||
interface PresentationReceiver : EventTarget {
|
||||
/*
|
||||
* Get the first connected presentation session in a receiving browsing
|
||||
* Get the first connected presentation connection in a receiving browsing
|
||||
* context.
|
||||
*/
|
||||
[Throws]
|
||||
Promise<PresentationSession> getSession();
|
||||
Promise<PresentationConnection> getConnection();
|
||||
|
||||
/*
|
||||
* Get all connected presentation sessions in a receiving browsing context.
|
||||
* Get all connected presentation connections in a receiving browsing context.
|
||||
*/
|
||||
[Throws]
|
||||
Promise<sequence<PresentationSession>> getSessions();
|
||||
Promise<sequence<PresentationConnection>> getConnections();
|
||||
|
||||
/*
|
||||
* It is called when an incoming session is connecting.
|
||||
* It is called when an incoming connection is connecting.
|
||||
*/
|
||||
attribute EventHandler onsessionavailable;
|
||||
attribute EventHandler onconnectionavailable;
|
||||
};
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
CheckAnyPermissions="presentation"]
|
||||
interface PresentationRequest : EventTarget {
|
||||
/*
|
||||
* A requesting page use start() to start a new session, and the session will
|
||||
* be returned with the promise. UA may show a prompt box with a list of
|
||||
* A requesting page use start() to start a new connection, and it will be
|
||||
* returned with the promise. UA may show a prompt box with a list of
|
||||
* available devices and ask the user to grant permission, choose a device, or
|
||||
* cancel the operation.
|
||||
*
|
||||
* The promise is resolved when the presenting page is successfully loaded and
|
||||
* the communication channel is established, i.e., the session state is
|
||||
* the communication channel is established, i.e., the connection state is
|
||||
* "connected".
|
||||
*
|
||||
* The promise may be rejected duo to one of the following reasons:
|
||||
|
@ -26,7 +26,7 @@ interface PresentationRequest : EventTarget {
|
|||
* - "TimeoutError": Presenting page takes too long to load.
|
||||
*/
|
||||
[Throws]
|
||||
Promise<PresentationSession> start();
|
||||
Promise<PresentationConnection> start();
|
||||
|
||||
/*
|
||||
* UA triggers device discovery mechanism periodically and monitor device
|
||||
|
@ -39,8 +39,8 @@ interface PresentationRequest : EventTarget {
|
|||
Promise<PresentationAvailability> getAvailability();
|
||||
|
||||
/*
|
||||
* It is called when a session associated with a PresentationRequest is created.
|
||||
* The event is fired for all sessions that are created for the controller.
|
||||
* It is called when a connection associated with a PresentationRequest is created.
|
||||
* The event is fired for all connections that are created for the controller.
|
||||
*/
|
||||
attribute EventHandler onsessionconnect;
|
||||
attribute EventHandler onconnectionavailable;
|
||||
};
|
||||
|
|
|
@ -374,10 +374,10 @@ WEBIDL_FILES = [
|
|||
'PositionError.webidl',
|
||||
'Presentation.webidl',
|
||||
'PresentationAvailability.webidl',
|
||||
'PresentationConnection.webidl',
|
||||
'PresentationDeviceInfoManager.webidl',
|
||||
'PresentationReceiver.webidl',
|
||||
'PresentationRequest.webidl',
|
||||
'PresentationSession.webidl',
|
||||
'ProcessingInstruction.webidl',
|
||||
'ProfileTimelineMarker.webidl',
|
||||
'Promise.webidl',
|
||||
|
@ -801,7 +801,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
|||
'PluginCrashedEvent.webidl',
|
||||
'PopStateEvent.webidl',
|
||||
'PopupBlockedEvent.webidl',
|
||||
'PresentationSessionConnectEvent.webidl',
|
||||
'PresentationConnectionAvailableEvent.webidl',
|
||||
'ProgressEvent.webidl',
|
||||
'RecordErrorEvent.webidl',
|
||||
'ScrollViewChangeEvent.webidl',
|
||||
|
|
|
@ -130,7 +130,7 @@ if (!imgTools)
|
|||
// different set of reference images. nsIXULRuntime.OS doesn't seem to be
|
||||
// available in xpcshell, so we'll use this as a kludgy way to figure out if
|
||||
// we're running on Windows.
|
||||
var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
|
||||
var isWindows = mozinfo.os == "win";
|
||||
|
||||
|
||||
/* ========== 1 ========== */
|
||||
|
|
|
@ -520,16 +520,6 @@ js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
|
|||
jsid id;
|
||||
if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
|
||||
JSObject* obj = &args.thisv().toObject();
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
if (obj->is<RegExpObject>() && id == NameToId(cx->names().source)) {
|
||||
if (JSScript* script = cx->currentScript()) {
|
||||
const char* filename = script->filename();
|
||||
cx->compartment()->addTelemetry(filename, JSCompartment::RegExpSourceProperty);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Shape* prop;
|
||||
if (obj->isNative() &&
|
||||
NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
|
||||
|
|
|
@ -8466,10 +8466,11 @@ CodeGenerator::visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic)
|
|||
|
||||
void
|
||||
CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
|
||||
PropertyName* name, TypedOrValueRegister output,
|
||||
bool monitoredResult, jsbytecode* profilerLeavePc)
|
||||
ConstantOrRegister id, TypedOrValueRegister output,
|
||||
bool monitoredResult, bool allowDoubleResult,
|
||||
jsbytecode* profilerLeavePc)
|
||||
{
|
||||
GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
|
||||
GetPropertyIC cache(liveRegs, objReg, id, output, monitoredResult, allowDoubleResult);
|
||||
cache.setProfilerLeavePC(profilerLeavePc);
|
||||
addCache(ins, allocateCache(cache));
|
||||
}
|
||||
|
@ -8497,17 +8498,30 @@ CodeGenerator::addSetElementCache(LInstruction* ins, Register obj, Register unbo
|
|||
addCache(ins, allocateCache(cache));
|
||||
}
|
||||
|
||||
ConstantOrRegister
|
||||
CodeGenerator::toConstantOrRegister(LInstruction* lir, size_t n, MIRType type)
|
||||
{
|
||||
if (type == MIRType_Value)
|
||||
return TypedOrValueRegister(ToValue(lir, n));
|
||||
|
||||
const LAllocation* value = lir->getOperand(n);
|
||||
if (value->isConstant())
|
||||
return ConstantOrRegister(*value->toConstant());
|
||||
|
||||
return TypedOrValueRegister(type, ToAnyRegister(value));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV* ins)
|
||||
{
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
Register objReg = ToRegister(ins->getOperand(0));
|
||||
PropertyName* name = ins->mir()->name();
|
||||
ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheV::Id, ins->mir()->idval()->type());
|
||||
bool monitoredResult = ins->mir()->monitoredResult();
|
||||
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
|
||||
|
||||
addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult,
|
||||
ins->mir()->profilerLeavePc());
|
||||
addGetPropertyCache(ins, liveRegs, objReg, id, output, monitoredResult,
|
||||
ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -8515,15 +8529,15 @@ CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT* ins)
|
|||
{
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
Register objReg = ToRegister(ins->getOperand(0));
|
||||
PropertyName* name = ins->mir()->name();
|
||||
ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheT::Id, ins->mir()->idval()->type());
|
||||
bool monitoredResult = ins->mir()->monitoredResult();
|
||||
TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
|
||||
|
||||
addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult,
|
||||
ins->mir()->profilerLeavePc());
|
||||
addGetPropertyCache(ins, liveRegs, objReg, id, output, monitoredResult,
|
||||
ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
|
||||
}
|
||||
|
||||
typedef bool (*GetPropertyICFn)(JSContext*, HandleScript, size_t, HandleObject,
|
||||
typedef bool (*GetPropertyICFn)(JSContext*, HandleScript, size_t, HandleObject, HandleValue,
|
||||
MutableHandleValue);
|
||||
const VMFunction GetPropertyIC::UpdateInfo = FunctionInfo<GetPropertyICFn>(GetPropertyIC::update);
|
||||
|
||||
|
@ -8541,6 +8555,7 @@ CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<GetProperty
|
|||
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(ic->id());
|
||||
pushArg(ic->object());
|
||||
pushArg(Imm32(ool->getCacheIndex()));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
|
@ -8551,62 +8566,6 @@ CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<GetProperty
|
|||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::addGetElementCache(LInstruction* ins, Register obj, TypedOrValueRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult,
|
||||
bool allowDoubleResult, jsbytecode* profilerLeavePc)
|
||||
{
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
|
||||
cache.setProfilerLeavePC(profilerLeavePc);
|
||||
addCache(ins, allocateCache(cache));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitGetElementCacheV(LGetElementCacheV* ins)
|
||||
{
|
||||
Register obj = ToRegister(ins->object());
|
||||
TypedOrValueRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
|
||||
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
|
||||
const MGetElementCache* mir = ins->mir();
|
||||
|
||||
addGetElementCache(ins, obj, index, output, mir->monitoredResult(),
|
||||
mir->allowDoubleResult(), mir->profilerLeavePc());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitGetElementCacheT(LGetElementCacheT* ins)
|
||||
{
|
||||
Register obj = ToRegister(ins->object());
|
||||
TypedOrValueRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
|
||||
TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
|
||||
const MGetElementCache* mir = ins->mir();
|
||||
|
||||
addGetElementCache(ins, obj, index, output, mir->monitoredResult(),
|
||||
mir->allowDoubleResult(), mir->profilerLeavePc());
|
||||
}
|
||||
|
||||
typedef bool (*GetElementICFn)(JSContext*, HandleScript, size_t, HandleObject, HandleValue,
|
||||
MutableHandleValue);
|
||||
const VMFunction GetElementIC::UpdateInfo = FunctionInfo<GetElementICFn>(GetElementIC::update);
|
||||
|
||||
void
|
||||
CodeGenerator::visitGetElementIC(OutOfLineUpdateCache* ool, DataPtr<GetElementIC>& ic)
|
||||
{
|
||||
LInstruction* lir = ool->lir();
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(ic->index());
|
||||
pushArg(ic->object());
|
||||
pushArg(Imm32(ool->getCacheIndex()));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
callVM(GetElementIC::UpdateInfo, lir);
|
||||
StoreValueTo(ic->output()).generate(this);
|
||||
restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSetElementCacheV(LSetElementCacheV* ins)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
void generateArgumentsChecks(bool bailout = true);
|
||||
bool generateBody();
|
||||
|
||||
ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
|
||||
|
||||
public:
|
||||
CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm = nullptr);
|
||||
~CodeGenerator();
|
||||
|
@ -355,8 +357,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
|
||||
void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
|
||||
void visitGetPropertyCacheT(LGetPropertyCacheT* ins);
|
||||
void visitGetElementCacheV(LGetElementCacheV* ins);
|
||||
void visitGetElementCacheT(LGetElementCacheT* ins);
|
||||
void visitSetElementCacheV(LSetElementCacheV* ins);
|
||||
void visitSetElementCacheT(LSetElementCacheT* ins);
|
||||
void visitBindNameCache(LBindNameCache* ins);
|
||||
|
@ -367,7 +367,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
|
||||
void visitGetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<GetPropertyIC>& ic);
|
||||
void visitSetPropertyIC(OutOfLineUpdateCache* ool, DataPtr<SetPropertyIC>& ic);
|
||||
void visitGetElementIC(OutOfLineUpdateCache* ool, DataPtr<GetElementIC>& ic);
|
||||
void visitSetElementIC(OutOfLineUpdateCache* ool, DataPtr<SetElementIC>& ic);
|
||||
void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
|
||||
void visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic);
|
||||
|
@ -397,11 +396,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
|
||||
private:
|
||||
void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
|
||||
PropertyName* name, TypedOrValueRegister output,
|
||||
bool monitoredResult, jsbytecode* profilerLeavePc);
|
||||
void addGetElementCache(LInstruction* ins, Register obj, TypedOrValueRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult,
|
||||
bool allowDoubleResult, jsbytecode* profilerLeavePc);
|
||||
ConstantOrRegister id, TypedOrValueRegister output,
|
||||
bool monitoredResult, bool allowDoubleResult,
|
||||
jsbytecode* profilerLeavePc);
|
||||
void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
|
||||
PropertyName* name, ConstantOrRegister value, bool strict,
|
||||
bool needsTypeBarrier, jsbytecode* profilerLeavePc);
|
||||
|
|
|
@ -6063,7 +6063,8 @@ IonBuilder::createThisScripted(MDefinition* callee, MDefinition* newTarget)
|
|||
// and thus invalidation.
|
||||
MInstruction* getProto;
|
||||
if (!invalidatedIdempotentCache()) {
|
||||
MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, names().prototype,
|
||||
MConstant* id = constant(StringValue(names().prototype));
|
||||
MGetPropertyCache* getPropCache = MGetPropertyCache::New(alloc(), newTarget, id,
|
||||
/* monitored = */ false);
|
||||
getPropCache->setIdempotent();
|
||||
getProto = getPropCache;
|
||||
|
@ -9097,8 +9098,8 @@ IonBuilder::getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index)
|
|||
if (index->mightBeType(MIRType_String) || index->mightBeType(MIRType_Symbol))
|
||||
barrier = BarrierKind::TypeSet;
|
||||
|
||||
MInstruction* ins = MGetElementCache::New(alloc(), obj, index, barrier == BarrierKind::TypeSet);
|
||||
|
||||
MGetPropertyCache* ins = MGetPropertyCache::New(alloc(), obj, index,
|
||||
barrier == BarrierKind::TypeSet);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
@ -10498,12 +10499,10 @@ IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache)
|
|||
}
|
||||
|
||||
bool
|
||||
IonBuilder::annotateGetPropertyCache(MDefinition* obj, MGetPropertyCache* getPropCache,
|
||||
TemporaryTypeSet* objTypes,
|
||||
IonBuilder::annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
|
||||
MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
|
||||
TemporaryTypeSet* pushedTypes)
|
||||
{
|
||||
PropertyName* name = getPropCache->name();
|
||||
|
||||
// Ensure every pushed value is a singleton.
|
||||
if (pushedTypes->unknownObject() || pushedTypes->baseFlags() != 0)
|
||||
return true;
|
||||
|
@ -11756,7 +11755,8 @@ IonBuilder::getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
|
|||
}
|
||||
}
|
||||
|
||||
MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, name,
|
||||
MConstant* id = constant(StringValue(name));
|
||||
MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, id,
|
||||
barrier == BarrierKind::TypeSet);
|
||||
|
||||
// Try to mark the cache as idempotent.
|
||||
|
@ -11779,7 +11779,7 @@ IonBuilder::getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
|
|||
// to do the GetPropertyCache, and we can dispatch based on the JSFunction
|
||||
// value.
|
||||
if (JSOp(*pc) == JSOP_CALLPROP && load->idempotent()) {
|
||||
if (!annotateGetPropertyCache(obj, load, obj->resultTypeSet(), types))
|
||||
if (!annotateGetPropertyCache(obj, name, load, obj->resultTypeSet(), types))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -956,8 +956,8 @@ class IonBuilder
|
|||
const BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
|
||||
bool isOwnProperty);
|
||||
|
||||
bool annotateGetPropertyCache(MDefinition* obj, MGetPropertyCache* getPropCache,
|
||||
TemporaryTypeSet* objTypes,
|
||||
bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name,
|
||||
MGetPropertyCache* getPropCache, TemporaryTypeSet* objTypes,
|
||||
TemporaryTypeSet* pushedTypes);
|
||||
|
||||
MGetPropertyCache* getInlineableGetPropertyCache(CallInfo& callInfo);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -29,7 +29,6 @@ class LInstruction;
|
|||
#define IONCACHE_KIND_LIST(_) \
|
||||
_(GetProperty) \
|
||||
_(SetProperty) \
|
||||
_(GetElement) \
|
||||
_(SetElement) \
|
||||
_(BindName) \
|
||||
_(Name)
|
||||
|
@ -382,37 +381,51 @@ class GetPropertyIC : public IonCache
|
|||
LiveRegisterSet liveRegs_;
|
||||
|
||||
Register object_;
|
||||
PropertyName* name_;
|
||||
ConstantOrRegister id_;
|
||||
TypedOrValueRegister output_;
|
||||
|
||||
// Only valid if idempotent
|
||||
size_t locationsIndex_;
|
||||
size_t numLocations_;
|
||||
|
||||
static const size_t MAX_FAILED_UPDATES = 16;
|
||||
uint16_t failedUpdates_;
|
||||
|
||||
bool monitoredResult_ : 1;
|
||||
bool allowDoubleResult_ : 1;
|
||||
bool hasTypedArrayLengthStub_ : 1;
|
||||
bool hasSharedTypedArrayLengthStub_ : 1;
|
||||
bool hasMappedArgumentsLengthStub_ : 1;
|
||||
bool hasUnmappedArgumentsLengthStub_ : 1;
|
||||
bool hasMappedArgumentsElementStub_ : 1;
|
||||
bool hasUnmappedArgumentsElementStub_ : 1;
|
||||
bool hasGenericProxyStub_ : 1;
|
||||
bool hasDenseStub_ : 1;
|
||||
|
||||
void emitIdGuard(MacroAssembler& masm, jsid id, Label* fail);
|
||||
|
||||
public:
|
||||
GetPropertyIC(LiveRegisterSet liveRegs,
|
||||
Register object, PropertyName* name,
|
||||
Register object, ConstantOrRegister id,
|
||||
TypedOrValueRegister output,
|
||||
bool monitoredResult)
|
||||
bool monitoredResult, bool allowDoubleResult)
|
||||
: liveRegs_(liveRegs),
|
||||
object_(object),
|
||||
name_(name),
|
||||
id_(id),
|
||||
output_(output),
|
||||
locationsIndex_(0),
|
||||
numLocations_(0),
|
||||
failedUpdates_(0),
|
||||
monitoredResult_(monitoredResult),
|
||||
allowDoubleResult_(allowDoubleResult),
|
||||
hasTypedArrayLengthStub_(false),
|
||||
hasSharedTypedArrayLengthStub_(false),
|
||||
hasMappedArgumentsLengthStub_(false),
|
||||
hasUnmappedArgumentsLengthStub_(false),
|
||||
hasGenericProxyStub_(false)
|
||||
hasMappedArgumentsElementStub_(false),
|
||||
hasUnmappedArgumentsElementStub_(false),
|
||||
hasGenericProxyStub_(false),
|
||||
hasDenseStub_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -423,8 +436,8 @@ class GetPropertyIC : public IonCache
|
|||
Register object() const {
|
||||
return object_;
|
||||
}
|
||||
PropertyName* name() const {
|
||||
return name_;
|
||||
ConstantOrRegister id() const {
|
||||
return id_;
|
||||
}
|
||||
TypedOrValueRegister output() const {
|
||||
return output_;
|
||||
|
@ -438,10 +451,21 @@ class GetPropertyIC : public IonCache
|
|||
bool hasArgumentsLengthStub(bool mapped) const {
|
||||
return mapped ? hasMappedArgumentsLengthStub_ : hasUnmappedArgumentsLengthStub_;
|
||||
}
|
||||
bool hasArgumentsElementStub(bool mapped) const {
|
||||
return mapped ? hasMappedArgumentsElementStub_ : hasUnmappedArgumentsElementStub_;
|
||||
}
|
||||
bool hasGenericProxyStub() const {
|
||||
return hasGenericProxyStub_;
|
||||
}
|
||||
|
||||
bool hasDenseStub() const {
|
||||
return hasDenseStub_;
|
||||
}
|
||||
void setHasDenseStub() {
|
||||
MOZ_ASSERT(!hasDenseStub());
|
||||
hasDenseStub_ = true;
|
||||
}
|
||||
|
||||
void setHasTypedArrayLengthStub(HandleObject obj) {
|
||||
if (obj->is<TypedArrayObject>()) {
|
||||
MOZ_ASSERT(!hasTypedArrayLengthStub_);
|
||||
|
@ -478,49 +502,64 @@ class GetPropertyIC : public IonCache
|
|||
return monitoredResult() && !idempotent();
|
||||
}
|
||||
|
||||
void maybeDisable(bool emitted);
|
||||
|
||||
// Attach the proper stub, if possible
|
||||
bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool* emitted);
|
||||
HandleObject obj, HandleValue idval, bool* emitted);
|
||||
|
||||
bool tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachGenericProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachDOMProxyShadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool resetNeeded,
|
||||
HandleObject obj, HandleId id, bool resetNeeded,
|
||||
void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachNative(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
|
||||
|
||||
bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name,
|
||||
void* returnAddr, bool* emitted);
|
||||
HandleObject obj, HandleId id, void* returnAddr,
|
||||
bool* emitted);
|
||||
|
||||
bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool* emitted);
|
||||
HandleObject obj, HandleId id, bool* emitted);
|
||||
|
||||
bool tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandlePropertyName name, bool* emitted);
|
||||
HandleObject obj, HandleId id, bool* emitted);
|
||||
|
||||
bool tryAttachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandleValue idval, bool* emitted);
|
||||
|
||||
bool tryAttachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandleValue idval, bool* emitted);
|
||||
|
||||
static bool canAttachDenseElementHole(JSObject* obj, HandleValue idval,
|
||||
TypedOrValueRegister output);
|
||||
bool tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, HandleValue idval, bool* emitted);
|
||||
|
||||
static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
|
||||
bool tryAttachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript,
|
||||
IonScript* ion, HandleObject obj,
|
||||
HandleValue idval, bool* emitted);
|
||||
|
||||
static bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
|
||||
HandleObject obj, MutableHandleValue vp);
|
||||
HandleObject obj, HandleValue id, MutableHandleValue vp);
|
||||
};
|
||||
|
||||
class SetPropertyIC : public IonCache
|
||||
|
@ -610,117 +649,6 @@ class SetPropertyIC : public IonCache
|
|||
HandleObject obj, HandleValue value);
|
||||
};
|
||||
|
||||
class GetElementIC : public IonCache
|
||||
{
|
||||
protected:
|
||||
LiveRegisterSet liveRegs_;
|
||||
|
||||
Register object_;
|
||||
TypedOrValueRegister index_;
|
||||
TypedOrValueRegister output_;
|
||||
|
||||
bool monitoredResult_ : 1;
|
||||
bool allowDoubleResult_ : 1;
|
||||
bool hasDenseStub_ : 1;
|
||||
bool hasMappedArgumentsStub_ : 1;
|
||||
bool hasUnmappedArgumentsStub_ : 1;
|
||||
|
||||
size_t failedUpdates_;
|
||||
|
||||
static const size_t MAX_FAILED_UPDATES;
|
||||
|
||||
public:
|
||||
GetElementIC(LiveRegisterSet liveRegs, Register object, TypedOrValueRegister index,
|
||||
TypedOrValueRegister output, bool monitoredResult, bool allowDoubleResult)
|
||||
: liveRegs_(liveRegs),
|
||||
object_(object),
|
||||
index_(index),
|
||||
output_(output),
|
||||
monitoredResult_(monitoredResult),
|
||||
allowDoubleResult_(allowDoubleResult),
|
||||
hasDenseStub_(false),
|
||||
hasMappedArgumentsStub_(false),
|
||||
hasUnmappedArgumentsStub_(false),
|
||||
failedUpdates_(0)
|
||||
{
|
||||
}
|
||||
|
||||
CACHE_HEADER(GetElement)
|
||||
|
||||
void reset(ReprotectCode reprotect);
|
||||
|
||||
Register object() const {
|
||||
return object_;
|
||||
}
|
||||
TypedOrValueRegister index() const {
|
||||
return index_;
|
||||
}
|
||||
TypedOrValueRegister output() const {
|
||||
return output_;
|
||||
}
|
||||
bool monitoredResult() const {
|
||||
return monitoredResult_;
|
||||
}
|
||||
bool allowDoubleResult() const {
|
||||
return allowDoubleResult_;
|
||||
}
|
||||
bool hasDenseStub() const {
|
||||
return hasDenseStub_;
|
||||
}
|
||||
bool hasArgumentsStub(bool mapped) const {
|
||||
return mapped ? hasMappedArgumentsStub_ : hasUnmappedArgumentsStub_;
|
||||
}
|
||||
void setHasDenseStub() {
|
||||
MOZ_ASSERT(!hasDenseStub());
|
||||
hasDenseStub_ = true;
|
||||
}
|
||||
|
||||
// Helpers for CanAttachNativeGetProp
|
||||
typedef JSContext * Context;
|
||||
bool allowGetters() const { MOZ_ASSERT(!idempotent()); return true; }
|
||||
bool allowArrayLength(Context) const { return false; }
|
||||
bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
|
||||
return monitoredResult();
|
||||
}
|
||||
|
||||
static bool canAttachGetProp(JSObject* obj, const Value& idval, jsid id);
|
||||
static bool canAttachDenseElement(JSObject* obj, const Value& idval);
|
||||
static bool canAttachDenseElementHole(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
|
||||
TypedOrValueRegister output);
|
||||
|
||||
bool attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, const Value& idval, HandlePropertyName name);
|
||||
|
||||
bool attachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, const Value& idval);
|
||||
|
||||
bool attachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj, const Value& idval);
|
||||
|
||||
bool attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject tarr, const Value& idval);
|
||||
|
||||
bool attachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||
HandleObject obj);
|
||||
|
||||
static bool
|
||||
update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
|
||||
HandleValue idval, MutableHandleValue vp);
|
||||
|
||||
void incFailedUpdates() {
|
||||
failedUpdates_++;
|
||||
}
|
||||
void resetFailedUpdates() {
|
||||
failedUpdates_ = 0;
|
||||
}
|
||||
bool shouldDisable() const {
|
||||
return !canAttachStub() ||
|
||||
(stubCount_ == 0 && failedUpdates_ > MAX_FAILED_UPDATES);
|
||||
}
|
||||
};
|
||||
|
||||
class SetElementIC : public IonCache
|
||||
{
|
||||
protected:
|
||||
|
|
|
@ -3252,6 +3252,12 @@ LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins)
|
|||
{
|
||||
MOZ_ASSERT(ins->object()->type() == MIRType_Object);
|
||||
|
||||
MDefinition* id = ins->idval();
|
||||
MOZ_ASSERT(id->type() == MIRType_String ||
|
||||
id->type() == MIRType_Symbol ||
|
||||
id->type() == MIRType_Int32 ||
|
||||
id->type() == MIRType_Value);
|
||||
|
||||
if (ins->monitoredResult()) {
|
||||
// Set the performs-call flag so that we don't omit the overrecursed
|
||||
// check. This is necessary because the cache can attach a scripted
|
||||
|
@ -3259,12 +3265,18 @@ LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins)
|
|||
gen->setPerformsCall();
|
||||
}
|
||||
|
||||
// If this is a GETPROP, the id is a constant string. Allow passing it as a
|
||||
// constant to reduce register allocation pressure.
|
||||
bool useConstId = id->type() == MIRType_String || id->type() == MIRType_Symbol;
|
||||
|
||||
if (ins->type() == MIRType_Value) {
|
||||
LGetPropertyCacheV* lir = new(alloc()) LGetPropertyCacheV(useRegister(ins->object()));
|
||||
useBoxOrTypedOrConstant(lir, LGetPropertyCacheV::Id, id, useConstId);
|
||||
defineBox(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
} else {
|
||||
LGetPropertyCacheT* lir = new(alloc()) LGetPropertyCacheT(useRegister(ins->object()));
|
||||
useBoxOrTypedOrConstant(lir, LGetPropertyCacheT::Id, id, useConstId);
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
@ -3310,29 +3322,6 @@ LIRGenerator::visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitGetElementCache(MGetElementCache* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->object()->type() == MIRType_Object);
|
||||
|
||||
if (ins->monitoredResult())
|
||||
gen->setPerformsCall(); // See visitGetPropertyCache.
|
||||
|
||||
if (ins->type() == MIRType_Value) {
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType_Value);
|
||||
LGetElementCacheV* lir = new(alloc()) LGetElementCacheV(useRegister(ins->object()));
|
||||
useBox(lir, LGetElementCacheV::Index, ins->index());
|
||||
defineBox(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
} else {
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
LGetElementCacheT* lir = new(alloc()) LGetElementCacheT(useRegister(ins->object()),
|
||||
useRegister(ins->index()));
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitBindNameCache(MBindNameCache* ins)
|
||||
{
|
||||
|
|
|
@ -226,7 +226,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitGetPropertyCache(MGetPropertyCache* ins);
|
||||
void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins);
|
||||
void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins);
|
||||
void visitGetElementCache(MGetElementCache* ins);
|
||||
void visitBindNameCache(MBindNameCache* ins);
|
||||
void visitGuardObjectIdentity(MGuardObjectIdentity* ins);
|
||||
void visitGuardClass(MGuardClass* ins);
|
||||
|
|
|
@ -4607,7 +4607,7 @@ MStoreTypedArrayElementStatic::base() const
|
|||
}
|
||||
|
||||
bool
|
||||
MGetElementCache::allowDoubleResult() const
|
||||
MGetPropertyCache::allowDoubleResult() const
|
||||
{
|
||||
if (!resultTypeSet())
|
||||
return true;
|
||||
|
@ -4668,7 +4668,8 @@ MGetPropertyCache::setBlock(MBasicBlock* block)
|
|||
}
|
||||
|
||||
bool
|
||||
MGetPropertyCache::updateForReplacement(MDefinition* ins) {
|
||||
MGetPropertyCache::updateForReplacement(MDefinition* ins)
|
||||
{
|
||||
MGetPropertyCache* other = ins->toGetPropertyCache();
|
||||
location_.append(&other->location_);
|
||||
return true;
|
||||
|
|
|
@ -10238,20 +10238,18 @@ class CacheLocationList : public InlineConcatList<CacheLocationList>
|
|||
};
|
||||
|
||||
class MGetPropertyCache
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>::Data
|
||||
{
|
||||
CompilerPropertyName name_;
|
||||
bool idempotent_;
|
||||
bool monitoredResult_;
|
||||
bool idempotent_ : 1;
|
||||
bool monitoredResult_ : 1;
|
||||
|
||||
CacheLocationList location_;
|
||||
|
||||
InlinePropertyTable* inlinePropertyTable_;
|
||||
|
||||
MGetPropertyCache(MDefinition* obj, PropertyName* name, bool monitoredResult)
|
||||
: MUnaryInstruction(obj),
|
||||
name_(name),
|
||||
MGetPropertyCache(MDefinition* obj, MDefinition* id, bool monitoredResult)
|
||||
: MBinaryInstruction(obj, id),
|
||||
idempotent_(false),
|
||||
monitoredResult_(monitoredResult),
|
||||
location_(),
|
||||
|
@ -10268,9 +10266,9 @@ class MGetPropertyCache
|
|||
public:
|
||||
INSTRUCTION_HEADER(GetPropertyCache)
|
||||
|
||||
static MGetPropertyCache* New(TempAllocator& alloc, MDefinition* obj, PropertyName* name,
|
||||
static MGetPropertyCache* New(TempAllocator& alloc, MDefinition* obj, MDefinition* id,
|
||||
bool monitoredResult) {
|
||||
return new(alloc) MGetPropertyCache(obj, name, monitoredResult);
|
||||
return new(alloc) MGetPropertyCache(obj, id, monitoredResult);
|
||||
}
|
||||
|
||||
InlinePropertyTable* initInlinePropertyTable(TempAllocator& alloc, jsbytecode* pc) {
|
||||
|
@ -10290,9 +10288,10 @@ class MGetPropertyCache
|
|||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
PropertyName* name() const {
|
||||
return name_;
|
||||
MDefinition* idval() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
|
||||
bool idempotent() const {
|
||||
return idempotent_;
|
||||
}
|
||||
|
@ -10312,8 +10311,6 @@ class MGetPropertyCache
|
|||
return false;
|
||||
if (!ins->isGetPropertyCache())
|
||||
return false;
|
||||
if (name() != ins->toGetPropertyCache()->name())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
|
||||
|
@ -10328,6 +10325,8 @@ class MGetPropertyCache
|
|||
|
||||
void setBlock(MBasicBlock* block) override;
|
||||
bool updateForReplacement(MDefinition* ins) override;
|
||||
|
||||
bool allowDoubleResult() const;
|
||||
};
|
||||
|
||||
// Emit code to load a value from an object if it matches one of the receivers
|
||||
|
@ -10646,44 +10645,6 @@ class MFunctionDispatch : public MDispatchInstruction
|
|||
}
|
||||
};
|
||||
|
||||
class MGetElementCache
|
||||
: public MBinaryInstruction
|
||||
{
|
||||
MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data PolicyV;
|
||||
MixPolicy<ObjectPolicy<0>, IntPolicy<1> >::Data PolicyT;
|
||||
TypePolicy* thisTypePolicy();
|
||||
|
||||
// See the comment in IonBuilder::jsop_getelem.
|
||||
bool monitoredResult_;
|
||||
|
||||
MGetElementCache(MDefinition* obj, MDefinition* value, bool monitoredResult)
|
||||
: MBinaryInstruction(obj, value), monitoredResult_(monitoredResult)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(GetElementCache)
|
||||
|
||||
static MGetElementCache* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
|
||||
bool monitoredResult)
|
||||
{
|
||||
return new(alloc) MGetElementCache(obj, value, monitoredResult);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition* index() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
bool monitoredResult() const {
|
||||
return monitoredResult_;
|
||||
}
|
||||
|
||||
bool allowDoubleResult() const;
|
||||
};
|
||||
|
||||
class MBindNameCache
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
|
|
|
@ -161,7 +161,6 @@ namespace jit {
|
|||
_(GetPropertyCache) \
|
||||
_(GetPropertyPolymorphic) \
|
||||
_(SetPropertyPolymorphic) \
|
||||
_(GetElementCache) \
|
||||
_(SetElementCache) \
|
||||
_(BindNameCache) \
|
||||
_(GuardShape) \
|
||||
|
|
|
@ -625,6 +625,23 @@ template bool BoxExceptPolicy<1, MIRType_String>::staticAdjustInputs(TempAllocat
|
|||
template bool BoxExceptPolicy<2, MIRType_String>::staticAdjustInputs(TempAllocator& alloc,
|
||||
MInstruction* ins);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
CacheIdPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
{
|
||||
MDefinition* in = ins->getOperand(Op);
|
||||
switch (in->type()) {
|
||||
case MIRType_Int32:
|
||||
case MIRType_String:
|
||||
case MIRType_Symbol:
|
||||
return true;
|
||||
default:
|
||||
return BoxPolicy<Op>::staticAdjustInputs(alloc, ins);
|
||||
}
|
||||
}
|
||||
|
||||
template bool CacheIdPolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||
|
||||
bool
|
||||
ToDoublePolicy::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
{
|
||||
|
@ -1209,6 +1226,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
|||
_(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >) \
|
||||
_(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >) \
|
||||
_(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >) \
|
||||
_(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>) \
|
||||
_(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >) \
|
||||
_(MixPolicy<ObjectPolicy<0>, IntPolicy<1> >) \
|
||||
_(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >) \
|
||||
|
@ -1275,14 +1293,6 @@ thisTypeSpecialization()
|
|||
|
||||
} // namespace
|
||||
|
||||
TypePolicy*
|
||||
MGetElementCache::thisTypePolicy()
|
||||
{
|
||||
if (type() == MIRType_Value)
|
||||
return PolicyV.thisTypePolicy();
|
||||
return PolicyT.thisTypePolicy();
|
||||
}
|
||||
|
||||
// For each MIR Instruction, this macro define the |typePolicy| method which is
|
||||
// using the |thisTypePolicy| method. The |thisTypePolicy| method is either a
|
||||
// member of the MIR Instruction, such as with MGetElementCache, a member
|
||||
|
|
|
@ -385,6 +385,18 @@ class BoxExceptPolicy final : public TypePolicy
|
|||
}
|
||||
};
|
||||
|
||||
// Box if not a typical property id (string, symbol, int32).
|
||||
template <unsigned Op>
|
||||
class CacheIdPolicy final : public TypePolicy
|
||||
{
|
||||
public:
|
||||
EMPTY_DATA_;
|
||||
static bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||
bool adjustInputs(TempAllocator& alloc, MInstruction* ins) {
|
||||
return staticAdjustInputs(alloc, ins);
|
||||
}
|
||||
};
|
||||
|
||||
// Combine multiple policies.
|
||||
template <class Lhs, class Rhs>
|
||||
class MixPolicy final : public TypePolicy
|
||||
|
|
|
@ -5482,11 +5482,13 @@ class LCallGetIntrinsicValue : public LCallInstructionHelper<BOX_PIECES, 0, 0>
|
|||
|
||||
// Patchable jump to stubs generated for a GetProperty cache, which loads a
|
||||
// boxed value.
|
||||
class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1 + BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GetPropertyCacheV)
|
||||
|
||||
static const size_t Id = 1;
|
||||
|
||||
explicit LGetPropertyCacheV(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
|
@ -5497,11 +5499,13 @@ class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1, 0>
|
|||
|
||||
// Patchable jump to stubs generated for a GetProperty cache, which loads a
|
||||
// value of a known type, possibly into an FP register.
|
||||
class LGetPropertyCacheT : public LInstructionHelper<1, 1, 0>
|
||||
class LGetPropertyCacheT : public LInstructionHelper<1, 1 + BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GetPropertyCacheT)
|
||||
|
||||
static const size_t Id = 1;
|
||||
|
||||
explicit LGetPropertyCacheT(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
|
@ -5613,47 +5617,6 @@ class LSetPropertyPolymorphicT : public LInstructionHelper<0, 2, 1>
|
|||
}
|
||||
};
|
||||
|
||||
class LGetElementCacheV : public LInstructionHelper<BOX_PIECES, 1 + BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GetElementCacheV)
|
||||
|
||||
static const size_t Index = 1;
|
||||
|
||||
explicit LGetElementCacheV(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const MGetElementCache* mir() const {
|
||||
return mir_->toGetElementCache();
|
||||
}
|
||||
};
|
||||
|
||||
class LGetElementCacheT : public LInstructionHelper<1, 2, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GetElementCacheT)
|
||||
|
||||
LGetElementCacheT(const LAllocation& object, const LAllocation& index) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, index);
|
||||
}
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation* index() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LDefinition* output() {
|
||||
return getDef(0);
|
||||
}
|
||||
const MGetElementCache* mir() const {
|
||||
return mir_->toGetElementCache();
|
||||
}
|
||||
};
|
||||
|
||||
class LBindNameCache : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -274,8 +274,6 @@
|
|||
_(GetPropertyCacheT) \
|
||||
_(GetPropertyPolymorphicV) \
|
||||
_(GetPropertyPolymorphicT) \
|
||||
_(GetElementCacheV) \
|
||||
_(GetElementCacheT) \
|
||||
_(BindNameCache) \
|
||||
_(CallGetProperty) \
|
||||
_(GetNameCache) \
|
||||
|
|
|
@ -619,6 +619,25 @@ LIRGeneratorShared::useBox(LInstruction* lir, size_t n, MDefinition* mir,
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorShared::useBoxOrTypedOrConstant(LInstruction* lir, size_t n, MDefinition* mir,
|
||||
bool useConstant)
|
||||
{
|
||||
if (mir->type() == MIRType_Value) {
|
||||
useBox(lir, n, mir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (useConstant && mir->isConstant())
|
||||
lir->setOperand(n, LAllocation(mir->toConstant()->vp()));
|
||||
else
|
||||
lir->setOperand(n, useRegister(mir));
|
||||
|
||||
#if defined(JS_NUNBOX32)
|
||||
lir->setOperand(n + 1, LAllocation());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -163,6 +163,11 @@ class LIRGeneratorShared : public MDefinitionVisitor
|
|||
inline void useBox(LInstruction* lir, size_t n, MDefinition* mir,
|
||||
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
|
||||
|
||||
// Adds a use at operand |n|. The use is either typed, a Value, or a
|
||||
// constant (if useConstant is true).
|
||||
inline void useBoxOrTypedOrConstant(LInstruction* lir, size_t n, MDefinition* mir,
|
||||
bool useConstant);
|
||||
|
||||
// Rather than defining a new virtual register, sets |ins| to have the same
|
||||
// virtual register as |as|.
|
||||
inline void redefine(MDefinition* ins, MDefinition* as);
|
||||
|
|
|
@ -753,7 +753,7 @@ struct JSCompartment
|
|||
// NO LONGER USING 5
|
||||
DeprecatedNoSuchMethod = 6, // JS 1.7+
|
||||
DeprecatedFlagsArgument = 7, // JS 1.3 or older
|
||||
RegExpSourceProperty = 8, // ES5
|
||||
// NO LONGER USING 8
|
||||
DeprecatedLanguageExtensionCount
|
||||
};
|
||||
|
||||
|
|
|
@ -2551,15 +2551,6 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
|||
return ok;
|
||||
}
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
if (obj->is<RegExpObject>() && id == NameToId(cx->names().source)) {
|
||||
if (JSScript* script = cx->currentScript()) {
|
||||
const char* filename = script->filename();
|
||||
cx->compartment()->addTelemetry(filename, JSCompartment::RegExpSourceProperty);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RootedNativeObject nobj(cx, obj.as<NativeObject>());
|
||||
RootedShape shape(cx);
|
||||
if (!NativeLookupOwnProperty<CanGC>(cx, nobj, id, &shape))
|
||||
|
|
|
@ -1673,19 +1673,19 @@
|
|||
/*
|
||||
* Bind the |this| value of a function to the supplied value.
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this => this
|
||||
* Category: Variables and Scopes
|
||||
* Type: This
|
||||
* Operands:
|
||||
* Stack: this => this
|
||||
*/ \
|
||||
macro(JSOP_SETTHIS , 163,"setthis", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
macro(JSOP_SETTHIS, 163,"setthis", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Find the function to invoke with |super()| on the scope chain.
|
||||
*
|
||||
* Category: Variables and Scopes
|
||||
* Type: Super
|
||||
* Operands:
|
||||
* Stack: => superFun
|
||||
* Category: Variables and Scopes
|
||||
* Type: Super
|
||||
* Operands:
|
||||
* Stack: => superFun
|
||||
*/ \
|
||||
macro(JSOP_SUPERFUN, 164,"superfun", NULL, 1, 0, 1, JOF_BYTE) \
|
||||
/*
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче