mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge latest green b2g-inbound changeset and mozilla-central; a=merge
This commit is contained in:
commit
ac1319df2b
@ -1522,7 +1522,7 @@ pref("browser.newtabpage.enhanced", false);
|
||||
pref("browser.newtabpage.rows", 3);
|
||||
|
||||
// number of columns of newtab grid
|
||||
pref("browser.newtabpage.columns", 8);
|
||||
pref("browser.newtabpage.columns", 5);
|
||||
|
||||
// directory tiles download URL
|
||||
pref("browser.newtabpage.directory.source", "chrome://global/content/directoryLinks.json");
|
||||
|
@ -3097,16 +3097,16 @@ const BrowserSearch = {
|
||||
}
|
||||
#endif
|
||||
let openSearchPageIfFieldIsNotActive = function(aSearchBar) {
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let url = doc.documentURI.toLowerCase();
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
if (url === "about:home") {
|
||||
AboutHome.focusInput(mm);
|
||||
} else if (url === "about:newtab") {
|
||||
ContentSearch.focusInput(mm);
|
||||
} else if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
||||
openUILinkIn("about:home", "current");
|
||||
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
||||
let url = gBrowser.currentURI.spec.toLowerCase();
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
if (url === "about:home") {
|
||||
AboutHome.focusInput(mm);
|
||||
} else if (url === "about:newtab") {
|
||||
ContentSearch.focusInput(mm);
|
||||
} else {
|
||||
openUILinkIn("about:home", "current");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -3319,6 +3319,7 @@ function FillHistoryMenu(aParent) {
|
||||
PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
|
||||
if (aURI) {
|
||||
let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
|
||||
iconURL = PlacesUtils.getImageURLForResolution(window, iconURL);
|
||||
item.style.listStyleImage = "url(" + iconURL + ")";
|
||||
}
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ addMessageListener("Browser:HideSessionRestoreButton", function (message) {
|
||||
let doc = content.document;
|
||||
let container;
|
||||
if (doc.documentURI.toLowerCase() == "about:home" &&
|
||||
(container = doc.getElementById("sessionRestoreContainer"))){
|
||||
(container = doc.getElementById("sessionRestoreContainer"))) {
|
||||
container.hidden = true;
|
||||
}
|
||||
});
|
||||
@ -90,18 +90,37 @@ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
|
||||
let AboutHomeListener = {
|
||||
init: function(chromeGlobal) {
|
||||
chromeGlobal.addEventListener('AboutHomeLoad', () => this.onPageLoad(), false, true);
|
||||
chromeGlobal.addEventListener('AboutHomeLoad', this, false, true);
|
||||
},
|
||||
|
||||
get isAboutHome() {
|
||||
return content.document.documentURI.toLowerCase() == "about:home";
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
if (!this.isAboutHome) {
|
||||
return;
|
||||
}
|
||||
switch (aEvent.type) {
|
||||
case "AboutHomeLoad":
|
||||
this.onPageLoad();
|
||||
break;
|
||||
case "AboutHomeSearchEvent":
|
||||
this.onSearch(aEvent);
|
||||
break;
|
||||
case "click":
|
||||
this.onClick(aEvent);
|
||||
break;
|
||||
case "pagehide":
|
||||
this.onPageHide(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (!this.isAboutHome) {
|
||||
return;
|
||||
}
|
||||
switch (aMessage.name) {
|
||||
case "AboutHome:Update":
|
||||
this.onUpdate(aMessage.data);
|
||||
@ -114,9 +133,6 @@ let AboutHomeListener = {
|
||||
|
||||
onUpdate: function(aData) {
|
||||
let doc = content.document;
|
||||
if (doc.documentURI.toLowerCase() != "about:home")
|
||||
return;
|
||||
|
||||
if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
|
||||
doc.getElementById("launcher").setAttribute("session", "true");
|
||||
|
||||
@ -133,25 +149,15 @@ let AboutHomeListener = {
|
||||
|
||||
onPageLoad: function() {
|
||||
let doc = content.document;
|
||||
if (doc.documentURI.toLowerCase() != "about:home" ||
|
||||
doc.documentElement.hasAttribute("hasBrowserHandlers")) {
|
||||
if (doc.documentElement.hasAttribute("hasBrowserHandlers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
||||
let self = this;
|
||||
addMessageListener("AboutHome:Update", self);
|
||||
addMessageListener("AboutHome:FocusInput", self);
|
||||
addEventListener("click", this.onClick, true);
|
||||
addEventListener("pagehide", function onPageHide(event) {
|
||||
if (event.target.defaultView.frameElement)
|
||||
return;
|
||||
removeMessageListener("AboutHome:Update", self);
|
||||
removeEventListener("click", self.onClick, true);
|
||||
removeEventListener("pagehide", onPageHide, true);
|
||||
if (event.target.documentElement)
|
||||
event.target.documentElement.removeAttribute("hasBrowserHandlers");
|
||||
}, true);
|
||||
addMessageListener("AboutHome:Update", this);
|
||||
addMessageListener("AboutHome:FocusInput", this);
|
||||
addEventListener("click", this, true);
|
||||
addEventListener("pagehide", this, true);
|
||||
|
||||
// XXX bug 738646 - when Marketplace is launched, remove this statement and
|
||||
// the hidden attribute set on the apps button in aboutHome.xhtml
|
||||
@ -160,10 +166,7 @@ let AboutHomeListener = {
|
||||
doc.getElementById("apps").removeAttribute("hidden");
|
||||
|
||||
sendAsyncMessage("AboutHome:RequestUpdate");
|
||||
|
||||
doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
|
||||
sendAsyncMessage("AboutHome:Search", { searchData: e.detail });
|
||||
}, true, true);
|
||||
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
|
||||
},
|
||||
|
||||
onClick: function(aEvent) {
|
||||
@ -217,8 +220,27 @@ let AboutHomeListener = {
|
||||
}
|
||||
},
|
||||
|
||||
onPageHide: function(aEvent) {
|
||||
if (event.target.defaultView.frameElement) {
|
||||
return;
|
||||
}
|
||||
removeMessageListener("AboutHome:Update", this);
|
||||
removeEventListener("click", this, true);
|
||||
removeEventListener("pagehide", this, true);
|
||||
if (event.target.documentElement) {
|
||||
event.target.documentElement.removeAttribute("hasBrowserHandlers");
|
||||
}
|
||||
},
|
||||
|
||||
onSearch: function(aEvent) {
|
||||
sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
|
||||
},
|
||||
|
||||
onFocusInput: function () {
|
||||
content.document.getElementById("searchText").focus();
|
||||
let searchInput = content.document.getElementById("searchText");
|
||||
if (searchInput) {
|
||||
searchInput.focus();
|
||||
}
|
||||
},
|
||||
};
|
||||
AboutHomeListener.init(this);
|
||||
|
@ -85,6 +85,9 @@
|
||||
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
|
||||
.getService(Components.interfaces.mozIPlacesAutoComplete);
|
||||
</field>
|
||||
<field name="PlacesUtils" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/PlacesUtils.jsm", {})).PlacesUtils;
|
||||
</field>
|
||||
<field name="mTabBox" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
|
||||
</field>
|
||||
@ -840,9 +843,7 @@
|
||||
|
||||
let sizedIconUrl = browser.mIconURL || "";
|
||||
if (sizedIconUrl) {
|
||||
let size = Math.round(16 * window.devicePixelRatio);
|
||||
sizedIconUrl += (sizedIconUrl.contains("#") ? "&" : "#") +
|
||||
"-moz-resolution=" + size + "," + size;
|
||||
sizedIconUrl = this.PlacesUtils.getImageURLForResolution(window, sizedIconUrl);
|
||||
}
|
||||
if (sizedIconUrl != aTab.getAttribute("image")) {
|
||||
if (sizedIconUrl)
|
||||
|
@ -420,8 +420,11 @@ let gTests = [
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Cmd+k should focus the search bar element",
|
||||
setup: function () {},
|
||||
desc: "Cmd+k should focus the search box in the page when the search box in the toolbar is absent",
|
||||
setup: function () {
|
||||
// Remove the search bar from toolbar
|
||||
CustomizableUI.removeWidgetFromArea("search-container");
|
||||
},
|
||||
run: Task.async(function* () {
|
||||
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
|
||||
let logo = doc.getElementById("brandLogo");
|
||||
@ -433,8 +436,25 @@ let gTests = [
|
||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||
yield promiseWaitForCondition(() => doc.activeElement === searchInput);
|
||||
is(searchInput, doc.activeElement, "Search input should be the active element.");
|
||||
CustomizableUI.reset();
|
||||
})
|
||||
},
|
||||
{
|
||||
desc: "Cmd+k should focus the search box in the toolbar when it's present",
|
||||
setup: function () {},
|
||||
run: Task.async(function* () {
|
||||
let logo = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementById("brandLogo");
|
||||
let doc = window.document;
|
||||
let searchInput = doc.getElementById("searchbar").textbox.inputField;
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(logo, {});
|
||||
isnot(searchInput, doc.activeElement, "Search bar should not be the active element.");
|
||||
|
||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||
yield promiseWaitForCondition(() => doc.activeElement === searchInput);
|
||||
is(searchInput, doc.activeElement, "Search bar should be the active element.");
|
||||
})
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
|
@ -17,8 +17,8 @@ function runTests() {
|
||||
// Expected length of grid
|
||||
let expectedValues = [1, 1, 1, 1, 8, 10];
|
||||
|
||||
// Values before setting new pref values (24 is the default value -> 8 x 3)
|
||||
let previousValues = [24, 1, 1, 1, 1, 8];
|
||||
// Values before setting new pref values (15 is the default value -> 5 x 3)
|
||||
let previousValues = [15, 1, 1, 1, 1, 8];
|
||||
|
||||
let existingTab, existingTabGridLength, newTab, newTabGridLength;
|
||||
yield addNewTabPageTab();
|
||||
|
@ -186,15 +186,24 @@ function runTests() {
|
||||
EventUtils.synthesizeKey("VK_DELETE", {});
|
||||
ok(table.hidden, "Search suggestion table hidden");
|
||||
|
||||
// Focus a different element than the search input.
|
||||
// Remove the search bar from toolbar
|
||||
CustomizableUI.removeWidgetFromArea("search-container");
|
||||
// Focus a different element than the search input from the page.
|
||||
let btn = getContentDocument().getElementById("newtab-customize-button");
|
||||
yield promiseClick(btn).then(TestRunner.next);
|
||||
|
||||
isnot(input, getContentDocument().activeElement, "Search input should not be focused");
|
||||
// Test that Ctrl/Cmd + K will focus the input field.
|
||||
// Test that Ctrl/Cmd + K will focus the input field from the page.
|
||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||
yield promiseSearchEvents(["FocusInput"]).then(TestRunner.next);
|
||||
is(input, getContentDocument().activeElement, "Search input should be focused");
|
||||
// Reset changes made to toolbar
|
||||
CustomizableUI.reset();
|
||||
|
||||
// Test that Ctrl/Cmd + K will focus the search bar from toolbar.
|
||||
let searchBar = gWindow.document.getElementById("searchbar");
|
||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||
is(searchBar.textbox.inputField, gWindow.document.activeElement, "Toolbar's search bar should be focused");
|
||||
|
||||
// Done. Revert the current engine and remove the new engines.
|
||||
Services.search.currentEngine = oldCurrentEngine;
|
||||
|
@ -159,6 +159,7 @@ const CustomizableWidgets = [{
|
||||
// Populate our list of history
|
||||
const kMaxResults = 15;
|
||||
let doc = aEvent.detail.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.excludeQueries = true;
|
||||
@ -201,8 +202,10 @@ const CustomizableWidgets = [{
|
||||
item.addEventListener("click", function (aEvent) {
|
||||
onHistoryVisit(uri, aEvent, item);
|
||||
});
|
||||
if (icon)
|
||||
item.setAttribute("image", "moz-anno:favicon:" + icon);
|
||||
if (icon) {
|
||||
let iconURL = PlacesUtils.getImageURLForResolution(win, "moz-anno:favicon:" + icon);
|
||||
item.setAttribute("image", iconURL);
|
||||
}
|
||||
fragment.appendChild(item);
|
||||
} catch (e) {
|
||||
ERROR("Error while showing history subview: " + e);
|
||||
|
@ -14,6 +14,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsOAuthClient.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MozLoopService"];
|
||||
|
||||
@ -56,6 +58,8 @@ let gHawkClient = null;
|
||||
let gRegisteredLoopServer = false;
|
||||
let gLocalizedStrings = null;
|
||||
let gInitializeTimer = null;
|
||||
let gFxAOAuthClientPromise = null;
|
||||
let gFxAOAuthClient = null;
|
||||
|
||||
/**
|
||||
* Internal helper methods and state
|
||||
@ -449,7 +453,86 @@ let MozLoopServiceInternal = {
|
||||
};
|
||||
|
||||
Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch Firefox Accounts (FxA) OAuth parameters from the Loop Server.
|
||||
*
|
||||
* @return {Promise} resolved with the body of the hawk request for OAuth parameters.
|
||||
*/
|
||||
promiseFxAOAuthParameters: function() {
|
||||
return this.hawkRequest("/fxa-oauth/params", "POST").then(response => {
|
||||
return JSON.parse(response.body);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the OAuth client constructed with Loop OAauth parameters.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
promiseFxAOAuthClient: Task.async(function* () {
|
||||
// We must make sure to have only a single client otherwise they will have different states and
|
||||
// multiple channels. This would happen if the user clicks the Login button more than once.
|
||||
if (gFxAOAuthClientPromise) {
|
||||
return gFxAOAuthClientPromise;
|
||||
}
|
||||
|
||||
gFxAOAuthClientPromise = this.promiseFxAOAuthParameters().then(
|
||||
parameters => {
|
||||
try {
|
||||
gFxAOAuthClient = new FxAccountsOAuthClient({
|
||||
parameters: parameters,
|
||||
});
|
||||
} catch (ex) {
|
||||
gFxAOAuthClientPromise = null;
|
||||
throw ex;
|
||||
}
|
||||
return gFxAOAuthClient;
|
||||
},
|
||||
error => {
|
||||
gFxAOAuthClientPromise = null;
|
||||
return error;
|
||||
}
|
||||
);
|
||||
|
||||
return gFxAOAuthClientPromise;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Params => web flow => code
|
||||
*/
|
||||
promiseFxAOAuthAuthorization: function() {
|
||||
let deferred = Promise.defer();
|
||||
this.promiseFxAOAuthClient().then(
|
||||
client => {
|
||||
client.onComplete = this._fxAOAuthComplete.bind(this, deferred);
|
||||
client.launchWebFlow();
|
||||
},
|
||||
error => {
|
||||
Cu.reportError(error);
|
||||
deferred.reject(error);
|
||||
}
|
||||
);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called once gFxAOAuthClient fires onComplete.
|
||||
*
|
||||
* @param {Deferred} deferred used to resolve or reject the gFxAOAuthClientPromise
|
||||
* @param {Object} result (with code and state)
|
||||
*/
|
||||
_fxAOAuthComplete: function(deferred, result) {
|
||||
gFxAOAuthClientPromise = null;
|
||||
|
||||
// Note: The state was already verified in FxAccountsOAuthClient.
|
||||
if (result) {
|
||||
deferred.resolve(result);
|
||||
} else {
|
||||
deferred.reject("Invalid token data");
|
||||
}
|
||||
},
|
||||
};
|
||||
Object.freeze(MozLoopServiceInternal);
|
||||
|
||||
@ -469,6 +552,17 @@ let gInitializeTimerFunc = () => {
|
||||
* Public API
|
||||
*/
|
||||
this.MozLoopService = {
|
||||
#ifdef DEBUG
|
||||
// Test-only helpers
|
||||
get internal() {
|
||||
return MozLoopServiceInternal;
|
||||
},
|
||||
|
||||
resetFxA: function() {
|
||||
gFxAOAuthClientPromise = null;
|
||||
},
|
||||
#endif
|
||||
|
||||
set initializeTimerFunc(value) {
|
||||
gInitializeTimerFunc = value;
|
||||
},
|
||||
@ -636,6 +730,19 @@ this.MozLoopService = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the FxA login flow using the OAuth client and params from the Loop server.
|
||||
*
|
||||
* The caller should be prepared to handle rejections related to network, server or login errors.
|
||||
*
|
||||
* @return {Promise} that resolves when the FxA login flow is complete.
|
||||
*/
|
||||
logInToFxA: function() {
|
||||
return MozLoopServiceInternal.promiseFxAOAuthAuthorization().then(response => {
|
||||
return MozLoopServiceInternal.promiseFxAOAuthToken(response.code, response.state);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs a hawk based request to the loop server.
|
||||
*
|
||||
|
@ -234,11 +234,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
var inputCSSClass = cx({
|
||||
"pending": this.state.pending,
|
||||
// Used in functional testing, signals that
|
||||
// call url was received from loop server
|
||||
"callUrl": !this.state.pending
|
||||
});
|
||||
return (
|
||||
PanelLayout({summary: __("share_link_header_text")},
|
||||
React.DOM.div({className: "invite"},
|
||||
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
|
||||
className: cx({pending: this.state.pending})}),
|
||||
className: inputCSSClass}),
|
||||
React.DOM.p({className: "button-group url-actions"},
|
||||
React.DOM.button({className: "btn btn-email", disabled: !this.state.callUrl,
|
||||
onClick: this.handleEmailButtonClick,
|
||||
|
@ -234,11 +234,17 @@ loop.panel = (function(_, mozL10n) {
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
var inputCSSClass = cx({
|
||||
"pending": this.state.pending,
|
||||
// Used in functional testing, signals that
|
||||
// call url was received from loop server
|
||||
"callUrl": !this.state.pending
|
||||
});
|
||||
return (
|
||||
<PanelLayout summary={__("share_link_header_text")}>
|
||||
<div className="invite">
|
||||
<input type="url" value={this.state.callUrl} readOnly="true"
|
||||
className={cx({pending: this.state.pending})} />
|
||||
className={inputCSSClass} />
|
||||
<p className="button-group url-actions">
|
||||
<button className="btn btn-email" disabled={!this.state.callUrl}
|
||||
onClick={this.handleEmailButtonClick}
|
||||
|
@ -179,9 +179,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var hangupButtonClasses = "btn btn-hangup";
|
||||
return (
|
||||
React.DOM.ul({className: "conversation-toolbar"},
|
||||
React.DOM.li(null, React.DOM.button({className: "btn btn-hangup",
|
||||
React.DOM.li(null, React.DOM.button({className: hangupButtonClasses,
|
||||
onClick: this.handleClickHangup,
|
||||
title: l10n.get("hangup_button_title")})),
|
||||
React.DOM.li(null, MediaControlButton({action: this.handleToggleVideo,
|
||||
|
@ -179,9 +179,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var hangupButtonClasses = "btn btn-hangup";
|
||||
return (
|
||||
<ul className="conversation-toolbar">
|
||||
<li><button className="btn btn-hangup"
|
||||
<li><button className={hangupButtonClasses}
|
||||
onClick={this.handleClickHangup}
|
||||
title={l10n.get("hangup_button_title")}></button></li>
|
||||
<li><MediaControlButton action={this.handleToggleVideo}
|
||||
|
@ -15,6 +15,9 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
EXTRA_JS_MODULES.loop += [
|
||||
'MozLoopAPI.jsm',
|
||||
'MozLoopPushHandler.jsm',
|
||||
'MozLoopService.jsm',
|
||||
'MozLoopWorker.js',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES.loop += [
|
||||
'MozLoopService.jsm',
|
||||
]
|
||||
|
@ -197,7 +197,6 @@ loop.webapp = (function($, _, OT, webL10n) {
|
||||
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||
var timestamp = date.toLocaleDateString(navigator.language, options);
|
||||
|
||||
this.setState({urlCreationDateString: timestamp});
|
||||
}
|
||||
},
|
||||
|
@ -197,7 +197,6 @@ loop.webapp = (function($, _, OT, webL10n) {
|
||||
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||
var timestamp = date.toLocaleDateString(navigator.language, options);
|
||||
|
||||
this.setState({urlCreationDateString: timestamp});
|
||||
}
|
||||
},
|
||||
|
20
browser/components/loop/test/functional/config.py
Normal file
20
browser/components/loop/test/functional/config.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Loop server configuration
|
||||
CONTENT_SERVER_PORT = 3001
|
||||
LOOP_SERVER_PORT = 5001
|
||||
FIREFOX_PREFERENCES = {
|
||||
"loop.server": "http://localhost:" + str(LOOP_SERVER_PORT),
|
||||
"browser.dom.window.dump.enabled": True,
|
||||
# Need to find the correct Pythonic syntax for this (unless just using
|
||||
# a string is the way to go), as well as find the bug with the
|
||||
# other preference for shunting ice to localhost that ekr/drno did,
|
||||
# and other stuff before we can support offline call tests
|
||||
# XXX "media.peerconnection.default_iceservers": "[]",
|
||||
"media.peerconnection.use_document_iceservers": False,
|
||||
"devtools.chrome.enabled": True,
|
||||
"devtools.debugger.prompt-connection": False,
|
||||
"devtools.debugger.remote-enabled": True,
|
||||
"media.volume_scale": "0",
|
||||
|
||||
# this dialog is fragile, and likely to introduce intermittent failures
|
||||
"media.navigator.permission.disabled": True
|
||||
}
|
@ -3,4 +3,4 @@ b2g = false
|
||||
browser = true
|
||||
qemu = false
|
||||
|
||||
[test_get_url.py]
|
||||
[test_1_browser_call.py]
|
||||
|
70
browser/components/loop/test/functional/serversetup.py
Normal file
70
browser/components/loop/test/functional/serversetup.py
Normal file
@ -0,0 +1,70 @@
|
||||
# Pull down and configure the loop server
|
||||
# Start the Loop server
|
||||
# Start the standalone server
|
||||
# Other util functions
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from mozprocess import processhandler
|
||||
|
||||
# XXX We want to convince ourselves that the race condition that importing
|
||||
# hanging_threads.py works around, tracked by bug 1046873, is gone, and get
|
||||
# rid of this inclusion entirely before we hook our functional tests up to
|
||||
# Tbpl, so that we don't introduce an intermittent failure.
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
import hanging_threads
|
||||
from config import *
|
||||
|
||||
CONTENT_SERVER_COMMAND = ["make", "runserver"]
|
||||
CONTENT_SERVER_ENV = os.environ.copy()
|
||||
# Set PORT so that it does not interfere with any other
|
||||
# development server that might be running
|
||||
CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
|
||||
"LOOP_SERVER_PORT": str(LOOP_SERVER_PORT)})
|
||||
|
||||
WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
|
||||
"/content/#call/{token}"
|
||||
|
||||
LOOP_SERVER_COMMAND = ["make", "runserver"]
|
||||
LOOP_SERVER_ENV = os.environ.copy()
|
||||
# Set PORT so that it does not interfere with any other
|
||||
# development server that might be running
|
||||
LOOP_SERVER_ENV.update({"NODE_ENV": "dev",
|
||||
"PORT": str(LOOP_SERVER_PORT),
|
||||
"WEB_APP_URL": WEB_APP_URL})
|
||||
|
||||
|
||||
class LoopTestServers:
|
||||
def __init__(self):
|
||||
self.loop_server = self.start_loop_server()
|
||||
self.content_server = self.start_content_server()
|
||||
|
||||
@staticmethod
|
||||
def start_loop_server():
|
||||
loop_server_location = os.environ.get('LOOP_SERVER')
|
||||
if loop_server_location is None:
|
||||
raise Exception('LOOP_SERVER variable not set')
|
||||
|
||||
os.chdir(loop_server_location)
|
||||
|
||||
p = processhandler.ProcessHandler(LOOP_SERVER_COMMAND,
|
||||
env=LOOP_SERVER_ENV)
|
||||
p.run()
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def start_content_server():
|
||||
content_server_location = os.path.join(os.path.dirname(__file__),
|
||||
"../../standalone")
|
||||
os.chdir(content_server_location)
|
||||
|
||||
p = processhandler.ProcessHandler(CONTENT_SERVER_COMMAND,
|
||||
env=CONTENT_SERVER_ENV)
|
||||
p.run()
|
||||
return p
|
||||
|
||||
def shutdown(self):
|
||||
self.content_server.kill()
|
||||
self.loop_server.kill()
|
||||
|
161
browser/components/loop/test/functional/test_1_browser_call.py
Normal file
161
browser/components/loop/test/functional/test_1_browser_call.py
Normal file
@ -0,0 +1,161 @@
|
||||
from marionette_test import MarionetteTestCase
|
||||
from by import By
|
||||
import urlparse
|
||||
from marionette.errors import NoSuchElementException, StaleElementException
|
||||
# noinspection PyUnresolvedReferences
|
||||
from marionette.wait import Wait
|
||||
from time import sleep
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from serversetup import LoopTestServers
|
||||
from config import *
|
||||
|
||||
|
||||
class Test1BrowserCall(MarionetteTestCase):
|
||||
# XXX Move this to setup class so it doesn't restart the server
|
||||
# after every test.
|
||||
def setUp(self):
|
||||
# start server
|
||||
self.loop_test_servers = LoopTestServers()
|
||||
|
||||
MarionetteTestCase.setUp(self)
|
||||
|
||||
# Unfortunately, enforcing preferences currently comes with the side
|
||||
# effect of launching and restarting the browser before running the
|
||||
# real functional tests. Bug 1048554 has been filed to track this.
|
||||
self.marionette.enforce_gecko_prefs(FIREFOX_PREFERENCES)
|
||||
|
||||
# this is browser chrome, kids, not the content window just yet
|
||||
self.marionette.set_context("chrome")
|
||||
|
||||
# taken from https://github.com/mozilla-b2g/gaia/blob/master/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L858
|
||||
# XXX factor out into utility object for use by other tests
|
||||
def wait_for_element_displayed(self, by, locator, timeout=None):
|
||||
Wait(self.marionette, timeout,
|
||||
ignored_exceptions=[NoSuchElementException, StaleElementException])\
|
||||
.until(lambda m: m.find_element(by, locator).is_displayed())
|
||||
return self.marionette.find_element(by, locator)
|
||||
|
||||
# XXX workaround for Marionette bug YYY
|
||||
def wait_for_element_exists(self, by, locator, timeout=None):
|
||||
Wait(self.marionette, timeout,
|
||||
ignored_exceptions=[NoSuchElementException, StaleElementException]) \
|
||||
.until(lambda m: m.find_element(by, locator))
|
||||
return self.marionette.find_element(by, locator)
|
||||
|
||||
def switch_to_panel(self):
|
||||
button = self.marionette.find_element(By.ID, "loop-call-button")
|
||||
|
||||
# click the element
|
||||
button.click()
|
||||
|
||||
# switch to the frame
|
||||
frame = self.marionette.find_element(By.ID, "loop")
|
||||
self.marionette.switch_to_frame(frame)
|
||||
|
||||
def load_and_verify_standalone_ui(self, url):
|
||||
self.marionette.set_context("content")
|
||||
self.marionette.navigate(url)
|
||||
|
||||
call_url_link = self.marionette.find_element(By.CLASS_NAME, "call-url") \
|
||||
.text
|
||||
self.assertEqual(url, call_url_link,
|
||||
"should be on the correct page")
|
||||
|
||||
def get_and_verify_call_url(self):
|
||||
# get and check for a call url
|
||||
url_input_element = self.wait_for_element_displayed(By.TAG_NAME,
|
||||
"input")
|
||||
|
||||
# wait for pending state to finish
|
||||
self.assertEqual(url_input_element.get_attribute("class"), "pending",
|
||||
"expect the input to be pending")
|
||||
|
||||
# get and check the input (the "callUrl" class is only added after
|
||||
# the pending class is removed and the URL has arrived).
|
||||
#
|
||||
# XXX should investigate getting rid of the fragile and otherwise
|
||||
# unnecessary callUrl class and replacing this with a By.CSS_SELECTOR
|
||||
# and some possible combination of :not and/or an attribute selector
|
||||
# once bug 1048551 is fixed.
|
||||
url_input_element = self.wait_for_element_displayed(By.CLASS_NAME,
|
||||
"callUrl")
|
||||
call_url = url_input_element.get_attribute("value")
|
||||
|
||||
self.assertNotEqual(call_url, u'',
|
||||
"input is populated with call URL after pending"
|
||||
" is finished")
|
||||
self.assertIn(urlparse.urlparse(call_url).scheme, ['http', 'https'],
|
||||
"call URL returned by server " + call_url +
|
||||
" has invalid scheme")
|
||||
return call_url
|
||||
|
||||
def start_and_verify_outgoing_call(self):
|
||||
# make the call!
|
||||
call_button = self.marionette.find_element(By.CLASS_NAME, "btn-success")
|
||||
call_button.click()
|
||||
|
||||
# expect a video container on standalone side
|
||||
video = self.wait_for_element_displayed(By.CLASS_NAME, "media")
|
||||
self.assertEqual(video.tag_name, "div", "expect a video container")
|
||||
|
||||
def accept_and_verify_incoming_call(self):
|
||||
self.marionette.set_context("chrome")
|
||||
self.marionette.switch_to_frame()
|
||||
|
||||
# XXX should be using wait_for_element_displayed, but need to wait
|
||||
# for Marionette bug 1055309 to be fixed.
|
||||
chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
|
||||
script = ("return document.getAnonymousElementByAttribute("
|
||||
"arguments[0], 'class', 'chat-frame');")
|
||||
frame = self.marionette.execute_script(script, [chatbox])
|
||||
self.marionette.switch_to_frame(frame)
|
||||
|
||||
# Accept the incoming call
|
||||
call_button = self.marionette.find_element(By.CLASS_NAME,
|
||||
"btn-success")
|
||||
# accept call from the desktop side
|
||||
call_button.click()
|
||||
|
||||
# expect a video container on desktop side
|
||||
video = self.wait_for_element_displayed(By.CLASS_NAME, "media")
|
||||
self.assertEqual(video.tag_name, "div", "expect a video container")
|
||||
|
||||
def hangup_call_and_verify_feedback(self):
|
||||
self.marionette.set_context("chrome")
|
||||
button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup")
|
||||
|
||||
# XXX For whatever reason, the click doesn't take effect unless we
|
||||
# wait for a bit (even if we wait for the element to actually be
|
||||
# displayed first, which we're not currently bothering with). It's
|
||||
# not entirely clear whether the click is being delivered in this case,
|
||||
# or whether there's a Marionette bug here.
|
||||
sleep(2)
|
||||
button.click()
|
||||
|
||||
# check that the feedback form is displayed
|
||||
feedback_form = self.wait_for_element_displayed(By.CLASS_NAME, "faces")
|
||||
self.assertEqual(feedback_form.tag_name, "div", "expect feedback form")
|
||||
|
||||
def test_1_browser_call(self):
|
||||
self.switch_to_panel()
|
||||
|
||||
call_url = self.get_and_verify_call_url()
|
||||
|
||||
# load the link clicker interface into the current content browser
|
||||
self.load_and_verify_standalone_ui(call_url)
|
||||
|
||||
self.start_and_verify_outgoing_call()
|
||||
|
||||
# Switch to the conversation window and answer
|
||||
self.accept_and_verify_incoming_call()
|
||||
|
||||
# hangup the call
|
||||
self.hangup_call_and_verify_feedback()
|
||||
|
||||
def tearDown(self):
|
||||
self.loop_test_servers.shutdown()
|
||||
MarionetteTestCase.tearDown(self)
|
@ -1,145 +0,0 @@
|
||||
from marionette_test import MarionetteTestCase
|
||||
from marionette.errors import NoSuchElementException
|
||||
from marionette.errors import StaleElementException
|
||||
from by import By
|
||||
# noinspection PyUnresolvedReferences
|
||||
from marionette.wait import Wait
|
||||
import os
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
from mozprocess import processhandler
|
||||
import urlparse
|
||||
|
||||
# XXX We want to convince ourselves that the race condition that importing
|
||||
# hanging_threads.py works around, tracked by bug 1046873, is gone, and get
|
||||
# rid of this inclusion entirely before we hook our functional tests up to
|
||||
# Tbpl, so that we don't introduce an intermittent failure.
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
import hanging_threads
|
||||
|
||||
CONTENT_SERVER_PORT = 3001
|
||||
LOOP_SERVER_PORT = 5001
|
||||
|
||||
CONTENT_SERVER_COMMAND = ["make", "runserver"]
|
||||
CONTENT_SERVER_ENV = os.environ.copy()
|
||||
# Set PORT so that it does not interfere with any other
|
||||
# development server that might be running
|
||||
CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
|
||||
"LOOP_SERVER_PORT": str(LOOP_SERVER_PORT)})
|
||||
|
||||
WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
|
||||
"/content/#call/{token}"
|
||||
|
||||
LOOP_SERVER_COMMAND = ["make", "runserver"]
|
||||
LOOP_SERVER_ENV = os.environ.copy()
|
||||
# Set PORT so that it does not interfere with any other
|
||||
# development server that might be running
|
||||
LOOP_SERVER_ENV.update({"NODE_ENV": "dev", "PORT": str(LOOP_SERVER_PORT),
|
||||
"WEB_APP_URL": WEB_APP_URL})
|
||||
|
||||
|
||||
class LoopTestServers:
|
||||
def __init__(self):
|
||||
self.loop_server = self.start_loop_server()
|
||||
self.content_server = self.start_content_server()
|
||||
|
||||
@staticmethod
|
||||
def start_loop_server():
|
||||
loop_server_location = os.environ.get('LOOP_SERVER')
|
||||
if loop_server_location is None:
|
||||
raise Exception('LOOP_SERVER variable not set')
|
||||
|
||||
os.chdir(loop_server_location)
|
||||
|
||||
p = processhandler.ProcessHandler(LOOP_SERVER_COMMAND,
|
||||
env=LOOP_SERVER_ENV)
|
||||
p.run()
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def start_content_server():
|
||||
content_server_location = os.path.join(os.path.dirname(__file__),
|
||||
"../../standalone")
|
||||
os.chdir(content_server_location)
|
||||
|
||||
p = processhandler.ProcessHandler(CONTENT_SERVER_COMMAND,
|
||||
env=CONTENT_SERVER_ENV)
|
||||
p.run()
|
||||
return p
|
||||
|
||||
def shutdown(self):
|
||||
self.content_server.kill()
|
||||
self.loop_server.kill()
|
||||
|
||||
|
||||
class TestGetUrl(MarionetteTestCase):
|
||||
# XXX Move this to setup class so it doesn't restart the server
|
||||
# after every test. This can happen when we write our second test,
|
||||
# expected to be in bug 976116.
|
||||
def setUp(self):
|
||||
# start server
|
||||
self.loop_test_servers = LoopTestServers()
|
||||
|
||||
MarionetteTestCase.setUp(self)
|
||||
|
||||
# Unfortunately, enforcing preferences currently comes with the side
|
||||
# effect of launching and restarting the browser before running the
|
||||
# real functional tests. Bug 1048554 has been filed to track this.
|
||||
preferences = {"loop.server": "http://localhost:" + str(LOOP_SERVER_PORT)}
|
||||
self.marionette.enforce_gecko_prefs(preferences)
|
||||
|
||||
# this is browser chrome, kids, not the content window just yet
|
||||
self.marionette.set_context("chrome")
|
||||
|
||||
def switch_to_panel(self):
|
||||
button = self.marionette.find_element(By.ID, "loop-call-button")
|
||||
|
||||
# click the element
|
||||
button.click()
|
||||
|
||||
# switch to the frame
|
||||
frame = self.marionette.find_element(By.ID, "loop")
|
||||
self.marionette.switch_to_frame(frame)
|
||||
|
||||
# taken from https://github.com/mozilla-b2g/gaia/blob/master/tests/python/gaia-ui-tests/gaiatest/gaia_test.py#L858
|
||||
# XXX factor out into utility object for use by other tests, this can
|
||||
# when we write our second test, in bug 976116
|
||||
def wait_for_element_displayed(self, by, locator, timeout=None):
|
||||
Wait(self.marionette, timeout,
|
||||
ignored_exceptions=[NoSuchElementException, StaleElementException])\
|
||||
.until(lambda m: m.find_element(by, locator).is_displayed())
|
||||
return self.marionette.find_element(by, locator)
|
||||
|
||||
def test_get_url(self):
|
||||
self.switch_to_panel()
|
||||
|
||||
# get and check for a call url
|
||||
url_input_element = self.wait_for_element_displayed(By.TAG_NAME,
|
||||
"input")
|
||||
|
||||
# wait for pending state to finish
|
||||
self.assertEqual(url_input_element.get_attribute("class"), "pending",
|
||||
"expect the input to be pending")
|
||||
|
||||
# get and check the input (the "callUrl" class is only added after
|
||||
# the pending class is removed and the URL has arrived).
|
||||
#
|
||||
# XXX should investigate getting rid of the fragile and otherwise
|
||||
# unnecessary callUrl class and replacing this with a By.CSS_SELECTOR
|
||||
# and some possible combination of :not and/or an attribute selector
|
||||
# once bug 1048551 is fixed.
|
||||
url_input_element = self.wait_for_element_displayed(By.CLASS_NAME,
|
||||
"callUrl")
|
||||
call_url = url_input_element.get_attribute("value")
|
||||
self.assertNotEqual(call_url, u'',
|
||||
"input is populated with call URL after pending"
|
||||
" is finished")
|
||||
|
||||
self.assertIn(urlparse.urlparse(call_url).scheme, ['http', 'https'],
|
||||
"call URL returned by server " + call_url +
|
||||
" has invalid scheme")
|
||||
|
||||
def tearDown(self):
|
||||
self.loop_test_servers.shutdown()
|
||||
MarionetteTestCase.tearDown(self)
|
@ -2,7 +2,10 @@
|
||||
support-files =
|
||||
head.js
|
||||
loop_fxa.sjs
|
||||
../../../../base/content/test/general/browser_fxa_oauth.html
|
||||
|
||||
[browser_fxa_login.js]
|
||||
skip-if = !debug
|
||||
[browser_loop_fxa_server.js]
|
||||
[browser_mozLoop_appVersionInfo.js]
|
||||
[browser_mozLoop_prefs.js]
|
||||
|
92
browser/components/loop/test/mochitest/browser_fxa_login.js
Normal file
92
browser/components/loop/test/mochitest/browser_fxa_login.js
Normal file
@ -0,0 +1,92 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test FxA logins with Loop.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
|
||||
|
||||
add_task(function* setup() {
|
||||
Services.prefs.setCharPref("loop.server", BASE_URL);
|
||||
Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
|
||||
registerCleanupFunction(function* () {
|
||||
info("cleanup time");
|
||||
yield promiseDeletedOAuthParams(BASE_URL);
|
||||
Services.prefs.clearUserPref("loop.server");
|
||||
Services.prefs.clearUserPref("services.push.serverURL");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* checkOAuthParams() {
|
||||
let params = {
|
||||
client_id: "client_id",
|
||||
content_uri: BASE_URL + "/content",
|
||||
oauth_uri: BASE_URL + "/oauth",
|
||||
profile_uri: BASE_URL + "/profile",
|
||||
state: "state",
|
||||
};
|
||||
yield promiseOAuthParamsSetup(BASE_URL, params);
|
||||
let client = yield MozLoopService.internal.promiseFxAOAuthClient();
|
||||
for (let key of Object.keys(params)) {
|
||||
ise(client.parameters[key], params[key], "Check " + key + " was passed to the OAuth client");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* basicAuthorization() {
|
||||
let result = yield MozLoopService.internal.promiseFxAOAuthAuthorization();
|
||||
is(result.code, "code1", "Check code");
|
||||
is(result.state, "state", "Check state");
|
||||
});
|
||||
|
||||
add_task(function* sameOAuthClientForTwoCalls() {
|
||||
MozLoopService.resetFxA();
|
||||
let client1 = yield MozLoopService.internal.promiseFxAOAuthClient();
|
||||
let client2 = yield MozLoopService.internal.promiseFxAOAuthClient();
|
||||
ise(client1, client2, "The same client should be returned");
|
||||
});
|
||||
|
||||
add_task(function* paramsInvalid() {
|
||||
MozLoopService.resetFxA();
|
||||
// Delete the params so an empty object is returned.
|
||||
yield promiseDeletedOAuthParams(BASE_URL);
|
||||
let result = null;
|
||||
let loginPromise = MozLoopService.logInToFxA();
|
||||
let caught = false;
|
||||
yield loginPromise.catch(() => {
|
||||
ok(true, "The login promise should be rejected due to invalid params");
|
||||
caught = true;
|
||||
});
|
||||
ok(caught, "Should have caught the rejection");
|
||||
is(result, null, "No token data should be returned");
|
||||
});
|
||||
|
||||
add_task(function* params_nonJSON() {
|
||||
MozLoopService.resetFxA();
|
||||
Services.prefs.setCharPref("loop.server", "https://loop.invalid");
|
||||
let result = null;
|
||||
let loginPromise = MozLoopService.logInToFxA();
|
||||
yield loginPromise.catch(() => {
|
||||
ok(true, "The login promise should be rejected due to non-JSON params");
|
||||
});
|
||||
is(result, null, "No token data should be returned");
|
||||
Services.prefs.setCharPref("loop.server", BASE_URL);
|
||||
});
|
||||
|
||||
add_task(function* invalidState() {
|
||||
MozLoopService.resetFxA();
|
||||
let params = {
|
||||
client_id: "client_id",
|
||||
content_uri: BASE_URL + "/content",
|
||||
oauth_uri: BASE_URL + "/oauth",
|
||||
profile_uri: BASE_URL + "/profile",
|
||||
state: "invalid_state",
|
||||
};
|
||||
yield promiseOAuthParamsSetup(BASE_URL, params);
|
||||
let loginPromise = MozLoopService.logInToFxA();
|
||||
yield loginPromise.catch((error) => {
|
||||
ok(error, "The login promise should be rejected due to invalid state");
|
||||
});
|
||||
});
|
@ -89,7 +89,7 @@ function promiseDeletedOAuthParams(baseURL) {
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open("DELETE", baseURL + "/setup_params", true);
|
||||
xhr.addEventListener("load", () => deferred.resolve(xhr));
|
||||
xhr.addEventListener("error", error => deferred.reject(error));
|
||||
xhr.addEventListener("error", deferred.reject);
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
|
@ -15,13 +15,17 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
* Entry point for HTTP requests.
|
||||
*/
|
||||
function handleRequest(request, response) {
|
||||
switch (request.queryString) {
|
||||
// Look at the query string but ignore past the encoded ? when deciding on the handler.
|
||||
switch (request.queryString.replace(/%3F.*/,"")) {
|
||||
case "/setup_params":
|
||||
setup_params(request, response);
|
||||
return;
|
||||
case "/fxa-oauth/params":
|
||||
params(request, response);
|
||||
return;
|
||||
case encodeURIComponent("/oauth/authorization"):
|
||||
oauth_authorization(request, response);
|
||||
return;
|
||||
case "/fxa-oauth/token":
|
||||
token(request, response);
|
||||
return;
|
||||
@ -89,6 +93,16 @@ function params(request, response) {
|
||||
response.write(JSON.stringify(params, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /oauth/authorization endpoint for the test params.
|
||||
*
|
||||
* Redirect to a test page that uses WebChannel to complete the web flow.
|
||||
*/
|
||||
function oauth_authorization(request, response) {
|
||||
response.setStatusLine(request.httpVersion, 302, "Found");
|
||||
response.setHeader("Location", "browser_fxa_oauth.html");
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /fxa-oauth/token
|
||||
*
|
||||
|
@ -1014,33 +1014,6 @@ this.PlacesUIUtils = {
|
||||
Weave.Service.engineManager.get("tabs") &&
|
||||
Weave.Service.engineManager.get("tabs").enabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the passed URL with a #moz-resolution fragment
|
||||
* for the specified dimensions and devicePixelRatio.
|
||||
*
|
||||
* @param aWindow
|
||||
* A window from where we want to get the device
|
||||
* pixel Ratio
|
||||
*
|
||||
* @param aURL
|
||||
* The URL where we should add the fragment
|
||||
*
|
||||
* @param aWidth
|
||||
* The target image width
|
||||
*
|
||||
* @param aHeight
|
||||
* The target image height
|
||||
*
|
||||
* @return The URL with the fragment at the end
|
||||
*/
|
||||
getImageURLForResolution:
|
||||
function PUIU_getImageURLForResolution(aWindow, aURL, aWidth = 16, aHeight = 16) {
|
||||
let width = Math.round(aWidth * aWindow.devicePixelRatio);
|
||||
let height = Math.round(aHeight * aWindow.devicePixelRatio);
|
||||
return aURL + (aURL.contains("#") ? "&" : "#") +
|
||||
"-moz-resolution=" + width + "," + height;
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
|
||||
|
@ -363,7 +363,7 @@ PlacesViewBase.prototype = {
|
||||
let icon = aPlacesNode.icon;
|
||||
if (icon)
|
||||
element.setAttribute("image",
|
||||
PlacesUIUtils.getImageURLForResolution(window, icon));
|
||||
PlacesUtils.getImageURLForResolution(window, icon));
|
||||
}
|
||||
|
||||
element._placesNode = aPlacesNode;
|
||||
@ -502,7 +502,7 @@ PlacesViewBase.prototype = {
|
||||
elt.removeAttribute("image");
|
||||
else if (icon != elt.getAttribute("image"))
|
||||
elt.setAttribute("image",
|
||||
PlacesUIUtils.getImageURLForResolution(window, icon));
|
||||
PlacesUtils.getImageURLForResolution(window, icon));
|
||||
},
|
||||
|
||||
nodeAnnotationChanged:
|
||||
@ -1019,7 +1019,7 @@ PlacesToolbar.prototype = {
|
||||
let icon = aChild.icon;
|
||||
if (icon)
|
||||
button.setAttribute("image",
|
||||
PlacesUIUtils.getImageURLForResolution(window, icon));
|
||||
PlacesUtils.getImageURLForResolution(window, icon));
|
||||
|
||||
if (PlacesUtils.containerTypes.indexOf(type) != -1) {
|
||||
button.setAttribute("type", "menu");
|
||||
@ -1844,7 +1844,7 @@ PlacesPanelMenuView.prototype = {
|
||||
let icon = aChild.icon;
|
||||
if (icon)
|
||||
button.setAttribute("image",
|
||||
PlacesUIUtils.getImageURLForResolution(window, icon));
|
||||
PlacesUtils.getImageURLForResolution(window, icon));
|
||||
|
||||
if (PlacesUtils.containerTypes.indexOf(type) != -1) {
|
||||
button.setAttribute("container", "true");
|
||||
|
@ -1396,7 +1396,11 @@ PlacesTreeView.prototype = {
|
||||
if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
|
||||
return "";
|
||||
|
||||
return this._getNodeForRow(aRow).icon;
|
||||
let node = this._getNodeForRow(aRow);
|
||||
if (PlacesUtils.nodeIsURI(node) && node.icon)
|
||||
return PlacesUtils.getImageURLForResolution(window, node.icon);
|
||||
|
||||
return node.icon;
|
||||
},
|
||||
|
||||
getProgressMode: function(aRow, aColumn) { },
|
||||
|
@ -125,6 +125,9 @@
|
||||
<field name="FormHistory" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
|
||||
</field>
|
||||
<field name="PlacesUtils" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/PlacesUtils.jsm", {})).PlacesUtils;
|
||||
</field>
|
||||
|
||||
<property name="engines" readonly="true">
|
||||
<getter><![CDATA[
|
||||
@ -292,9 +295,7 @@
|
||||
<parameter name="uri"/>
|
||||
<body><![CDATA[
|
||||
if (uri) {
|
||||
let size = Math.round(16 * window.devicePixelRatio);
|
||||
if (!uri.contains("#"))
|
||||
uri += "#-moz-resolution=" + size + "," + size;
|
||||
uri = this.PlacesUtils.getImageURLForResolution(window, uri);
|
||||
}
|
||||
element.setAttribute("src", uri);
|
||||
]]></body>
|
||||
|
@ -13,6 +13,7 @@ var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
@ -45,7 +46,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
||||
let element = doc.createElementNS(kNSXUL, aTagName);
|
||||
element.setAttribute("label", closedTabs[i].title);
|
||||
if (closedTabs[i].image) {
|
||||
setImage(closedTabs[i], element);
|
||||
setImage(aWindow, closedTabs[i], element);
|
||||
}
|
||||
element.setAttribute("value", i);
|
||||
if (aTagName == "menuitem") {
|
||||
@ -117,7 +118,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
||||
item.setAttribute("label", menuLabel);
|
||||
let selectedTab = undoItem.tabs[undoItem.selected - 1];
|
||||
if (selectedTab.image) {
|
||||
setImage(selectedTab, item);
|
||||
setImage(aWindow, selectedTab, item);
|
||||
}
|
||||
if (aTagName == "menuitem") {
|
||||
item.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
|
||||
@ -167,10 +168,11 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = {
|
||||
},
|
||||
};
|
||||
|
||||
function setImage(aItem, aElement) {
|
||||
let iconURL = aItem.image;
|
||||
function setImage(aWindow, aItem, aElement) {
|
||||
let iconURL = PlacesUtils.getImageURLForResolution(aWindow, aItem.image);
|
||||
// don't initiate a connection just to fetch a favicon (see bug 467828)
|
||||
if (/^https?:/.test(iconURL))
|
||||
iconURL = "moz-anno:favicon:" + iconURL;
|
||||
|
||||
aElement.setAttribute("image", iconURL);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
* 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/. */
|
||||
|
||||
Components.utils.import('resource://gre/modules/PlacesUtils.jsm');
|
||||
|
||||
let FavIcons = {
|
||||
// Pref that controls whether to display site icons.
|
||||
PREF_CHROME_SITE_ICONS: "browser.chrome.site_icons",
|
||||
@ -87,6 +89,8 @@ let FavIcons = {
|
||||
tabImage = this._favIconService.getFaviconLinkForIcon(tabImageURI).spec;
|
||||
}
|
||||
|
||||
tabImage = PlacesUtils.getImageURLForResolution(window, tabImage);
|
||||
|
||||
callback(tabImage);
|
||||
},
|
||||
|
||||
@ -99,7 +103,9 @@ let FavIcons = {
|
||||
let {currentURI} = tab.linkedBrowser;
|
||||
this._favIconService.getFaviconURLForPage(currentURI, function (uri) {
|
||||
if (uri) {
|
||||
callback(this._favIconService.getFaviconLinkForIcon(uri).spec);
|
||||
let icon = PlacesUtils.getImageURLForResolution(window,
|
||||
this._favIconService.getFaviconLinkForIcon(uri).spec);
|
||||
callback(icon);
|
||||
} else {
|
||||
callback(this.defaultFavicon);
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ function test() {
|
||||
let newState = {
|
||||
windows: [{
|
||||
tabs: [{
|
||||
entries: [{ url: "http://www.google.com" }],
|
||||
entries: [{ url: "about:mozilla" }],
|
||||
hidden: true,
|
||||
attributes: {},
|
||||
extData: {
|
||||
"tabview-tab":
|
||||
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
|
||||
'"userSize":null,"url":"http://www.google.com","groupID":1,' +
|
||||
'"userSize":null,"url":"about:mozilla","groupID":1,' +
|
||||
'"imageData":null,"title":null}'
|
||||
}
|
||||
},{
|
||||
|
@ -23,6 +23,13 @@ function test() {
|
||||
yield changeManifestValue("name", "the best app");
|
||||
yield changeManifestValueBad("name", "the worst app");
|
||||
yield addNewManifestProperty("developer", "foo", "bar");
|
||||
|
||||
// add duplicate property in the same parent doesn't create duplicates
|
||||
yield addNewManifestProperty("developer", "foo", "bar2");
|
||||
|
||||
// add propery with same key in other parent is allowed
|
||||
yield addNewManifestProperty("tester", "foo", "new");
|
||||
|
||||
yield addNewManifestPropertyBad("developer", "blob", "bob");
|
||||
yield removeManifestProperty("developer", "foo");
|
||||
gManifestWindow = null;
|
||||
@ -94,15 +101,14 @@ function changeManifestValueBad(key, value) {
|
||||
}
|
||||
|
||||
function addNewManifestProperty(parent, key, value) {
|
||||
info("Adding new property - parent: " + parent + "; key: " + key + "; value: " + value + "\n\n");
|
||||
return Task.spawn(function() {
|
||||
let parentElem = gManifestWindow.document
|
||||
.querySelector("[id ^= '" + parent + "']");
|
||||
ok(parentElem,
|
||||
"Found parent element");
|
||||
let addPropertyElem = parentElem
|
||||
.querySelector(".variables-view-add-property");
|
||||
ok(addPropertyElem,
|
||||
"Found add-property button");
|
||||
ok(parentElem, "Found parent element: " + parentElem.id);
|
||||
|
||||
let addPropertyElem = parentElem.querySelector(".variables-view-add-property");
|
||||
ok(addPropertyElem, "Found add-property button");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow);
|
||||
|
||||
@ -116,10 +122,13 @@ function addNewManifestProperty(parent, key, value) {
|
||||
|
||||
yield waitForUpdate();
|
||||
|
||||
let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
|
||||
parentElem = gManifestWindow.document.querySelector("[id ^= '" + parent + "']");
|
||||
let elems = parentElem.querySelectorAll("[id ^= '" + key + "']");
|
||||
is(elems.length, 1, "No duplicate property is added");
|
||||
|
||||
let newElem = elems[0];
|
||||
let nameElem = newElem.querySelector(".name");
|
||||
is(nameElem.value, key,
|
||||
"Key doesn't match expected Key");
|
||||
is(nameElem.value, key, "Key doesn't match expected Key");
|
||||
|
||||
ok(key in gManifestEditor.manifest[parent],
|
||||
"Manifest doesn't contain expected key");
|
||||
|
@ -2,5 +2,8 @@
|
||||
"name": "My packaged app",
|
||||
"developer": {
|
||||
"name": "Foo Bar"
|
||||
},
|
||||
"tester" : {
|
||||
"who": "qa"
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,16 @@ function connect() {
|
||||
});
|
||||
}
|
||||
|
||||
// Certain options should be toggled since we can assume chrome debugging here
|
||||
function setPrefDefaults() {
|
||||
Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
|
||||
Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
let cmdClose = document.getElementById("toolbox-cmd-close");
|
||||
cmdClose.addEventListener("command", onCloseCommand);
|
||||
setPrefDefaults();
|
||||
connect();
|
||||
});
|
||||
|
||||
|
@ -295,7 +295,6 @@ function MonitorClient(client, form) {
|
||||
MonitorClient.prototype.destroy = function () {
|
||||
this.client.unregisterClient(this);
|
||||
}
|
||||
MonitorClient.prototype.detach = function () {}
|
||||
MonitorClient.prototype.start = function () {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
|
@ -8,7 +8,12 @@ const CLIENT_EVENT_TYPE = "ContentSearchClient";
|
||||
|
||||
// Forward events from the in-content service to the test.
|
||||
content.addEventListener(SERVICE_EVENT_TYPE, event => {
|
||||
sendAsyncMessage(TEST_MSG, event.detail);
|
||||
// The event dispatch code in content.js clones the event detail into the
|
||||
// content scope. That's generally the right thing, but causes us to end
|
||||
// up with an XrayWrapper to it here, which will screw us up when trying to
|
||||
// serialize the object in sendAsyncMessage. Waive Xrays for the benefit of
|
||||
// the test machinery.
|
||||
sendAsyncMessage(TEST_MSG, Components.utils.waiveXrays(event.detail));
|
||||
});
|
||||
|
||||
// Forward messages from the test to the in-content service.
|
||||
|
@ -65,9 +65,10 @@
|
||||
#newtab-customize-button {
|
||||
background-color: transparent;
|
||||
background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 32, 32, 0);
|
||||
background-size: 28px;
|
||||
border: none;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
#newtab-customize-button:-moz-any(:hover, :active, [active]) {
|
||||
@ -166,9 +167,10 @@
|
||||
/* CONTROLS */
|
||||
.newtab-control {
|
||||
background-color: transparent;
|
||||
background-size: 24px;
|
||||
border: none;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.newtab-control-pin,
|
||||
@ -199,10 +201,9 @@
|
||||
}
|
||||
|
||||
.newtab-control-sponsored {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-image: url(chrome://browser/skin/newtab/controls.png);
|
||||
background-position: -249px -1px;
|
||||
background-size: auto;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
|
@ -2632,6 +2632,7 @@ nsContentUtils::GenerateStateKey(nsIContent* aContent,
|
||||
nsIPrincipal*
|
||||
nsContentUtils::SubjectPrincipal()
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
JSContext* cx = GetCurrentJSContext();
|
||||
if (!cx) {
|
||||
return GetSystemPrincipal();
|
||||
@ -4697,6 +4698,7 @@ nsContentUtils::CheckSecurityBeforeLoad(nsIURI* aURIToLoad,
|
||||
bool
|
||||
nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
return aPrincipal == sSystemPrincipal;
|
||||
}
|
||||
|
||||
@ -4710,6 +4712,7 @@ nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal)
|
||||
nsIPrincipal*
|
||||
nsContentUtils::GetSystemPrincipal()
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
return sSystemPrincipal;
|
||||
}
|
||||
|
||||
@ -5521,6 +5524,7 @@ JSContext *
|
||||
nsContentUtils::GetCurrentJSContext()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
return sXPConnect->GetCurrentJSContext();
|
||||
}
|
||||
|
||||
@ -5529,6 +5533,7 @@ JSContext *
|
||||
nsContentUtils::GetSafeJSContext()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
return sXPConnect->GetSafeJSContext();
|
||||
}
|
||||
|
||||
@ -5536,6 +5541,7 @@ nsContentUtils::GetSafeJSContext()
|
||||
JSContext *
|
||||
nsContentUtils::GetDefaultJSContextForThread()
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
return GetSafeJSContext();
|
||||
} else {
|
||||
@ -5547,6 +5553,7 @@ nsContentUtils::GetDefaultJSContextForThread()
|
||||
JSContext *
|
||||
nsContentUtils::GetCurrentJSContextForThread()
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
return GetCurrentJSContext();
|
||||
} else {
|
||||
|
@ -107,9 +107,7 @@ nsDOMFileReader::Init()
|
||||
// Instead of grabbing some random global from the context stack,
|
||||
// let's use the default one (junk scope) for now.
|
||||
// We should move away from this Init...
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
BindToOwner(global);
|
||||
BindToOwner(xpc::GetNativeForGlobal(xpc::PrivilegedJunkScope()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
@ -2142,7 +2143,7 @@ nsDocument::Init()
|
||||
// we use the default compartment for this document, instead of creating
|
||||
// wrapper in some random compartment when the document is exposed to js
|
||||
// via some events.
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetNativeForGlobal(xpc::PrivilegedJunkScope());
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
mScopeObject = do_GetWeakReference(global);
|
||||
MOZ_ASSERT(mScopeObject);
|
||||
@ -4494,7 +4495,7 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->MaybeStartControlling(this);
|
||||
mMaybeServiceWorkerControlled = true;
|
||||
@ -8526,7 +8527,7 @@ nsDocument::Destroy()
|
||||
|
||||
mRegistry = nullptr;
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->MaybeStopControlling(this);
|
||||
}
|
||||
|
@ -352,9 +352,7 @@ nsXMLHttpRequest::Init()
|
||||
// Instead of grabbing some random global from the context stack,
|
||||
// let's use the default one (junk scope) for now.
|
||||
// We should move away from this Init...
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
Construct(subjectPrincipal, global);
|
||||
Construct(subjectPrincipal, xpc::GetNativeForGlobal(xpc::PrivilegedJunkScope()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,6 @@ LOCAL_INCLUDES += [
|
||||
'/dom/canvas',
|
||||
'/dom/xbl',
|
||||
'/editor/libeditor',
|
||||
'/editor/libeditor/text',
|
||||
'/editor/txmgr',
|
||||
'/layout/forms',
|
||||
'/layout/generic',
|
||||
|
@ -261,6 +261,24 @@ AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
aChannelInterpretation));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetPassThrough(bool aPassThrough)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, bool aPassThrough)
|
||||
: ControlMessage(aStream), mPassThrough(aPassThrough) {}
|
||||
virtual void Run()
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
|
||||
}
|
||||
bool mPassThrough;
|
||||
};
|
||||
|
||||
MOZ_ASSERT(this);
|
||||
GraphImpl()->AppendMessage(new Message(this, aPassThrough));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMode,
|
||||
@ -453,10 +471,15 @@ AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
|
||||
ObtainInputBlock(inputChunks[i], i);
|
||||
}
|
||||
bool finished = false;
|
||||
if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
|
||||
mEngine->ProcessBlock(this, inputChunks[0], &mLastChunks[0], &finished);
|
||||
if (mPassThrough) {
|
||||
MOZ_ASSERT(outputCount == 1, "For now, we only support nodes that have one output port");
|
||||
mLastChunks[0] = inputChunks[0];
|
||||
} else {
|
||||
mEngine->ProcessBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
|
||||
if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
|
||||
mEngine->ProcessBlock(this, inputChunks[0], &mLastChunks[0], &finished);
|
||||
} else {
|
||||
mEngine->ProcessBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
|
||||
}
|
||||
}
|
||||
for (uint16_t i = 0; i < outputCount; ++i) {
|
||||
NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
|
||||
|
@ -54,7 +54,8 @@ public:
|
||||
mKind(aKind),
|
||||
mNumberOfInputChannels(2),
|
||||
mMarkAsFinishedAfterThisBlock(false),
|
||||
mAudioParamStream(false)
|
||||
mAudioParamStream(false),
|
||||
mPassThrough(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mChannelCountMode = ChannelCountMode::Max;
|
||||
@ -85,6 +86,7 @@ public:
|
||||
void SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMoe,
|
||||
ChannelInterpretation aChannelInterpretation);
|
||||
void SetPassThrough(bool aPassThrough);
|
||||
ChannelInterpretation GetChannelInterpretation()
|
||||
{
|
||||
return mChannelInterpretation;
|
||||
@ -192,6 +194,8 @@ protected:
|
||||
bool mMarkAsFinishedAfterThisBlock;
|
||||
// Whether the stream is an AudioParamHelper stream.
|
||||
bool mAudioParamStream;
|
||||
// Whether the stream just passes its input through.
|
||||
bool mPassThrough;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ public:
|
||||
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
|
||||
// than in every reader than inherits from MediaDecoderReader.
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
|
||||
virtual int64_t GetEvictionOffset(double aTime) { return -1; }
|
||||
|
||||
virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
|
||||
virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
|
||||
|
@ -109,9 +109,10 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
|
||||
, mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
|
||||
, mTimeRangesMonitor("MP4Reader::TimeRanges")
|
||||
, mDemuxerInitialized(false)
|
||||
, mIsEncrypted(false)
|
||||
, mIndexReady(false)
|
||||
, mIndexMonitor("MP4 index")
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(MP4Reader);
|
||||
@ -304,6 +305,11 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
||||
bool ok = mDemuxer->Init();
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
mIndexReady = true;
|
||||
}
|
||||
|
||||
mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasValidVideo();
|
||||
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
|
||||
// If we have video, we *only* allow H.264 to be decoded.
|
||||
@ -417,6 +423,8 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
||||
*aInfo = mInfo;
|
||||
*aTags = nullptr;
|
||||
|
||||
UpdateIndex();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -774,28 +782,52 @@ MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||
void
|
||||
MP4Reader::UpdateIndex()
|
||||
{
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
nsTArray<Interval<Microseconds>> timeRanges;
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
if (!mIndexReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
resource->Pin();
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
if (NS_SUCCEEDED(resource->GetCachedRanges(ranges))) {
|
||||
mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
|
||||
mDemuxer->UpdateIndex(ranges);
|
||||
}
|
||||
resource->Unpin();
|
||||
|
||||
MonitorAutoLock mon(mTimeRangesMonitor);
|
||||
mTimeRanges = timeRanges;
|
||||
}
|
||||
|
||||
int64_t
|
||||
MP4Reader::GetEvictionOffset(double aTime)
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
if (!mIndexReady) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mDemuxer->GetEvictionOffset(aTime * 1000000.0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
|
||||
{
|
||||
MonitorAutoLock mon(mTimeRangesMonitor);
|
||||
for (size_t i = 0; i < mTimeRanges.Length(); i++) {
|
||||
aBuffered->Add((mTimeRanges[i].start - aStartTime) / 1000000.0,
|
||||
(mTimeRanges[i].end - aStartTime) / 1000000.0);
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
if (!mIndexReady) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaResource* resource = mDecoder->GetResource();
|
||||
nsTArray<MediaByteRange> ranges;
|
||||
resource->Pin();
|
||||
nsresult rv = resource->GetCachedRanges(ranges);
|
||||
resource->Unpin();
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsTArray<Interval<Microseconds>> timeRanges;
|
||||
mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
|
||||
for (size_t i = 0; i < timeRanges.Length(); i++) {
|
||||
aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0,
|
||||
(timeRanges[i].end - aStartTime) / 1000000.0);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -55,6 +55,8 @@ public:
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength,
|
||||
int64_t aOffset) MOZ_OVERRIDE;
|
||||
|
||||
virtual int64_t GetEvictionOffset(double aTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
|
||||
int64_t aStartTime) MOZ_OVERRIDE;
|
||||
|
||||
@ -181,14 +183,15 @@ private:
|
||||
layers::LayersBackend mLayersBackendType;
|
||||
|
||||
nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
|
||||
Monitor mTimeRangesMonitor;
|
||||
nsTArray<mp4_demuxer::Interval<Microseconds>> mTimeRanges;
|
||||
|
||||
// True if we've read the streams' metadata.
|
||||
bool mDemuxerInitialized;
|
||||
|
||||
// Synchronized by decoder monitor.
|
||||
bool mIsEncrypted;
|
||||
|
||||
bool mIndexReady;
|
||||
Monitor mIndexMonitor;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -112,11 +112,16 @@ SubBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
|
||||
int64_t
|
||||
SubBufferDecoder::ConvertToByteOffset(double aTime)
|
||||
{
|
||||
int64_t readerOffset = mReader->GetEvictionOffset(aTime);
|
||||
if (readerOffset >= 0) {
|
||||
return readerOffset;
|
||||
}
|
||||
|
||||
// Uses a conversion based on (aTime/duration) * length. For the
|
||||
// purposes of eviction this should be adequate since we have the
|
||||
// byte threshold as well to ensure data actually gets evicted and
|
||||
// we ensure we don't evict before the current playable point.
|
||||
if (mMediaDuration == -1) {
|
||||
if (mMediaDuration <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int64_t length = GetResource()->GetLength();
|
||||
|
@ -65,6 +65,7 @@ AudioNode::AudioNode(AudioContext* aContext,
|
||||
, mChannelCountMode(aChannelCountMode)
|
||||
, mChannelInterpretation(aChannelInterpretation)
|
||||
, mId(gId++)
|
||||
, mPassThrough(false)
|
||||
#ifdef DEBUG
|
||||
, mDemiseNotified(false)
|
||||
#endif
|
||||
@ -416,5 +417,22 @@ AudioNode::RemoveOutputParam(AudioParam* aParam)
|
||||
mOutputParams.RemoveElement(aParam);
|
||||
}
|
||||
|
||||
bool
|
||||
AudioNode::PassThrough() const
|
||||
{
|
||||
MOZ_ASSERT(NumberOfInputs() == 1 && NumberOfOutputs() == 1);
|
||||
return mPassThrough;
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SetPassThrough(bool aPassThrough)
|
||||
{
|
||||
MOZ_ASSERT(NumberOfInputs() == 1 && NumberOfOutputs() == 1);
|
||||
mPassThrough = aPassThrough;
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetPassThrough(mPassThrough);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,9 @@ public:
|
||||
|
||||
uint32_t Id() const { return mId; }
|
||||
|
||||
bool PassThrough() const;
|
||||
void SetPassThrough(bool aPassThrough);
|
||||
|
||||
uint32_t ChannelCount() const { return mChannelCount; }
|
||||
virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
|
||||
{
|
||||
@ -267,6 +270,9 @@ private:
|
||||
ChannelCountMode mChannelCountMode;
|
||||
ChannelInterpretation mChannelInterpretation;
|
||||
const uint32_t mId;
|
||||
// Whether the node just passes through its input. This is a devtools API that
|
||||
// only works for some node types.
|
||||
bool mPassThrough;
|
||||
#ifdef DEBUG
|
||||
// In debug builds, check to make sure that the node demise notification has
|
||||
// been properly sent before the node is destroyed.
|
||||
|
@ -24,6 +24,8 @@ support-files =
|
||||
webaudio.js
|
||||
|
||||
[test_analyserNode.html]
|
||||
[test_analyserNodeOutput.html]
|
||||
[test_analyserNodePassThrough.html]
|
||||
[test_AudioBuffer.html]
|
||||
[test_audioBufferSourceNode.html]
|
||||
[test_audioBufferSourceNodeEnded.html]
|
||||
@ -49,6 +51,7 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
|
||||
[test_audioParamTimelineDestinationOffset.html]
|
||||
[test_badConnect.html]
|
||||
[test_biquadFilterNode.html]
|
||||
[test_biquadFilterNodePassThrough.html]
|
||||
[test_biquadFilterNodeWithGain.html]
|
||||
[test_bug808374.html]
|
||||
[test_bug827541.html]
|
||||
@ -74,6 +77,7 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
|
||||
[test_convolverNode.html]
|
||||
[test_convolverNode_mono_mono.html]
|
||||
[test_convolverNodeChannelCount.html]
|
||||
[test_convolverNodePassThrough.html]
|
||||
[test_convolverNodeWithGain.html]
|
||||
[test_currentTime.html]
|
||||
[test_decodeMultichannel.html]
|
||||
@ -81,6 +85,7 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
|
||||
[test_delayNodeAtMax.html]
|
||||
[test_delayNodeChannelChanges.html]
|
||||
[test_delayNodeCycles.html]
|
||||
[test_delayNodePassThrough.html]
|
||||
[test_delayNodeSmallMaxDelay.html]
|
||||
[test_delayNodeTailIncrease.html]
|
||||
[test_delayNodeTailWithDisconnect.html]
|
||||
@ -88,8 +93,10 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
|
||||
[test_delayNodeTailWithReconnect.html]
|
||||
[test_delayNodeWithGain.html]
|
||||
[test_dynamicsCompressorNode.html]
|
||||
[test_dynamicsCompressorNodePassThrough.html]
|
||||
[test_gainNode.html]
|
||||
[test_gainNodeInLoop.html]
|
||||
[test_gainNodePassThrough.html]
|
||||
[test_maxChannelCount.html]
|
||||
[test_mediaDecoding.html]
|
||||
[test_mediaElementAudioSourceNode.html]
|
||||
@ -125,4 +132,5 @@ skip-if = (toolkit == 'gonk' && !debug)
|
||||
[test_waveDecoder.html]
|
||||
[test_waveShaper.html]
|
||||
[test_waveShaperNoCurve.html]
|
||||
[test_waveShaperPassThrough.html]
|
||||
[test_waveShaperZeroLengthCurve.html]
|
||||
|
43
content/media/webaudio/test/test_analyserNodeOutput.html
Normal file
43
content/media/webaudio/test/test_analyserNodeOutput.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var analyser = context.createAnalyser();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(analyser);
|
||||
|
||||
source.start(0);
|
||||
return analyser;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AnalyserNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var analyser = context.createAnalyser();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(analyser);
|
||||
|
||||
var analyserWrapped = SpecialPowers.wrap(analyser);
|
||||
ok("passThrough" in analyserWrapped, "AnalyserNode should support the passThrough API");
|
||||
analyserWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return analyser;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test BiquadFilterNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var filter = context.createBiquadFilter();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(filter);
|
||||
|
||||
var filterWrapped = SpecialPowers.wrap(filter);
|
||||
ok("passThrough" in filterWrapped, "BiquadFilterNode should support the passThrough API");
|
||||
filterWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return filter;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test ConvolverNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var convolver = context.createConvolver();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
convolver.buffer = this.buffer;
|
||||
|
||||
source.connect(convolver);
|
||||
|
||||
var convolverWrapped = SpecialPowers.wrap(convolver);
|
||||
ok("passThrough" in convolverWrapped, "ConvolverNode should support the passThrough API");
|
||||
convolverWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return convolver;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
53
content/media/webaudio/test/test_delayNodePassThrough.html
Normal file
53
content/media/webaudio/test/test_delayNodePassThrough.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test DelayNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="webaudio.js" type="text/javascript"></script>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var gTest = {
|
||||
length: 4096,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var delay = context.createDelay();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(delay);
|
||||
|
||||
delay.delayTime.value = 0.5;
|
||||
|
||||
// Delay the source stream by 2048 frames
|
||||
delay.delayTime.value = 2048 / context.sampleRate;
|
||||
|
||||
var delayWrapped = SpecialPowers.wrap(delay);
|
||||
ok("passThrough" in delayWrapped, "DelayNode should support the passThrough API");
|
||||
delayWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return delay;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
var silence = context.createBuffer(1, 2048, context.sampleRate);
|
||||
|
||||
return [this.buffer, silence];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test DynamicsCompressorNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var compressor = context.createDynamicsCompressor();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(compressor);
|
||||
|
||||
var compressorWrapped = SpecialPowers.wrap(compressor);
|
||||
ok("passThrough" in compressorWrapped, "DynamicsCompressorNode should support the passThrough API");
|
||||
compressorWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return compressor;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
49
content/media/webaudio/test/test_gainNodePassThrough.html
Normal file
49
content/media/webaudio/test/test_gainNodePassThrough.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test GainNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
gain.gain.value = 0.5;
|
||||
|
||||
var gainWrapped = SpecialPowers.wrap(gain);
|
||||
ok("passThrough" in gainWrapped, "GainNode should support the passThrough API");
|
||||
gainWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
53
content/media/webaudio/test/test_pannerNodePassThrough.html
Normal file
53
content/media/webaudio/test/test_pannerNodePassThrough.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test PannerNode with passthrough</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 gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
|
||||
var panner = context.createPanner();
|
||||
|
||||
source.buffer = this.buffer;
|
||||
|
||||
source.connect(panner);
|
||||
|
||||
context.listener.setOrientation(0, 6.311749985202524e+307, 0, 0.1, 1000, 0);
|
||||
context.listener.setOrientation(0, 0, -6.311749985202524e+307, 0, 0, 6.311749985202524e+307);
|
||||
panner.setPosition(2, 0, 0);
|
||||
panner.rolloffFactor = 0;
|
||||
panner.panningModel = "equalpower";
|
||||
|
||||
var pannerWrapped = SpecialPowers.wrap(panner);
|
||||
ok("passThrough" in pannerWrapped, "PannerNode should support the passThrough API");
|
||||
pannerWrapped.passThrough = true;
|
||||
|
||||
source.start(0);
|
||||
return panner;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
55
content/media/webaudio/test/test_waveShaperPassThrough.html
Normal file
55
content/media/webaudio/test/test_waveShaperPassThrough.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test WaveShaperNode with passthrough</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 gTest = {
|
||||
length: 4096,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = this.buffer;
|
||||
|
||||
var shaper = context.createWaveShaper();
|
||||
shaper.curve = this.curve;
|
||||
|
||||
var shaperWrapped = SpecialPowers.wrap(shaper);
|
||||
ok("passThrough" in shaperWrapped, "WaveShaperNode should support the passThrough API");
|
||||
shaperWrapped.passThrough = true;
|
||||
|
||||
source.connect(shaper);
|
||||
|
||||
source.start(0);
|
||||
return shaper;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.buffer = context.createBuffer(1, 4096, context.sampleRate);
|
||||
for (var i = 1; i < 4095; ++i) {
|
||||
this.buffer.getChannelData(0)[i] = 2 * (i / 4096) - 1;
|
||||
}
|
||||
// Two out of range values
|
||||
this.buffer.getChannelData(0)[0] = -2;
|
||||
this.buffer.getChannelData(0)[4095] = 2;
|
||||
|
||||
this.curve = new Float32Array(2048);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
this.curve[i] = Math.sin(100 * Math.PI * (i + 1) / context.sampleRate);
|
||||
}
|
||||
|
||||
return [this.buffer];
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -2531,7 +2531,7 @@ nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
|
||||
{
|
||||
NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED);
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> global(cx, xpc::GetCompilationScope());
|
||||
JS::Rooted<JSObject*> global(cx, xpc::CompilationScope());
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
@ -2553,8 +2553,7 @@ nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
|
||||
// been set.
|
||||
JS::Handle<JSScript*> script =
|
||||
JS::Handle<JSScript*>::fromMarkedLocation(mScriptObject.address());
|
||||
// Note - Inverting the order of these operands is a rooting hazard.
|
||||
MOZ_ASSERT(xpc::GetCompilationScope() == JS::CurrentGlobalOrNull(cx));
|
||||
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
|
||||
return nsContentUtils::XPConnect()->WriteScript(aStream, cx,
|
||||
xpc_UnmarkGrayScript(script));
|
||||
}
|
||||
@ -2621,7 +2620,7 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
|
||||
aStream->Read32(&mLangVersion);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> global(cx, xpc::GetCompilationScope());
|
||||
JS::Rooted<JSObject*> global(cx, xpc::CompilationScope());
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
@ -2731,7 +2730,7 @@ NotifyOffThreadScriptCompletedRunnable::Run()
|
||||
JSScript *script;
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, xpc::GetCompilationScope());
|
||||
JSAutoCompartment ac(cx, xpc::CompilationScope());
|
||||
script = JS::FinishOffThreadScript(cx, JS_GetRuntime(cx), mToken);
|
||||
}
|
||||
|
||||
@ -2757,9 +2756,8 @@ nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
|
||||
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
|
||||
{
|
||||
// We'll compile the script in the compilation scope.
|
||||
NS_ENSURE_TRUE(xpc::GetCompilationScope(), NS_ERROR_UNEXPECTED);
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, xpc::GetCompilationScope());
|
||||
JSAutoCompartment ac(cx, xpc::CompilationScope());
|
||||
|
||||
nsAutoCString urlspec;
|
||||
nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec);
|
||||
|
@ -1113,7 +1113,7 @@ Console::ProcessCallData(ConsoleCallData* aData)
|
||||
// mStorage, but that's a bit fragile. Instead, we just use the junk scope,
|
||||
// with explicit permission from the XPConnect module owner. If you're
|
||||
// tempted to do that anywhere else, talk to said module owner first.
|
||||
JSAutoCompartment ac2(cx, xpc::GetJunkScope());
|
||||
JSAutoCompartment ac2(cx, xpc::PrivilegedJunkScope());
|
||||
|
||||
JS::Rooted<JS::Value> eventValue(cx);
|
||||
if (!ToJSValue(cx, event, &eventValue)) {
|
||||
|
@ -499,7 +499,7 @@ ThreadsafeAutoJSContext::operator JSContext*() const
|
||||
|
||||
AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
|
||||
: AutoJSContext(true MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
|
||||
, mAc(mCx, XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal())
|
||||
, mAc(mCx, xpc::UnprivilegedJunkScope())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ nsJSUtils::ReportPendingException(JSContext *aContext)
|
||||
// The SafeJSContext has no default object associated with it.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aContext == nsContentUtils::GetSafeJSContext());
|
||||
scope = xpc::GetSafeJSContextGlobal();
|
||||
scope = xpc::UnprivilegedJunkScope(); // Usage approved by bholley
|
||||
}
|
||||
JSAutoCompartment ac(aContext, scope);
|
||||
JS_ReportPendingException(aContext);
|
||||
|
@ -81,6 +81,13 @@ GetPluginMimeTypes(const nsTArray<nsRefPtr<nsPluginElement> >& aPlugins,
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
operator<(const nsRefPtr<nsMimeType>& lhs, const nsRefPtr<nsMimeType>& rhs)
|
||||
{
|
||||
// Sort MIME types alphabetically by type name.
|
||||
return lhs->Type() < rhs->Type();
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginArray::GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes,
|
||||
nsTArray<nsRefPtr<nsMimeType> >& aHiddenMimeTypes)
|
||||
@ -96,6 +103,10 @@ nsPluginArray::GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes,
|
||||
|
||||
GetPluginMimeTypes(mPlugins, aMimeTypes);
|
||||
GetPluginMimeTypes(mHiddenPlugins, aHiddenMimeTypes);
|
||||
|
||||
// Alphabetize the enumeration order of non-hidden MIME types to reduce
|
||||
// fingerprintable entropy based on plugins' installation file times.
|
||||
aMimeTypes.Sort();
|
||||
}
|
||||
|
||||
nsPluginElement*
|
||||
@ -297,6 +308,14 @@ IsPluginEnumerable(const nsTArray<nsCString>& enumerableNames,
|
||||
return false; // hide plugin!
|
||||
}
|
||||
|
||||
static bool
|
||||
operator<(const nsRefPtr<nsPluginElement>& lhs,
|
||||
const nsRefPtr<nsPluginElement>& rhs)
|
||||
{
|
||||
// Sort plugins alphabetically by name.
|
||||
return lhs->PluginTag()->mName < rhs->PluginTag()->mName;
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginArray::EnsurePlugins()
|
||||
{
|
||||
@ -345,6 +364,10 @@ nsPluginArray::EnsurePlugins()
|
||||
|
||||
pluginArray.AppendElement(new nsPluginElement(mWindow, pluginTag));
|
||||
}
|
||||
|
||||
// Alphabetize the enumeration order of non-hidden plugins to reduce
|
||||
// fingerprintable entropy based on plugins' installation file times.
|
||||
mPlugins.Sort();
|
||||
}
|
||||
|
||||
// nsPluginElement implementation.
|
||||
|
@ -11104,7 +11104,7 @@ class CGDictionary(CGThing):
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext *cx = jsapi.cx();
|
||||
JSAutoCompartment ac(cx, xpc::GetSafeJSContextGlobal()); // Usage approved by bholley
|
||||
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley
|
||||
JS::Rooted<JS::Value> obj(cx);
|
||||
return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON);
|
||||
"""))
|
||||
@ -11892,7 +11892,7 @@ class CGBindingRoot(CGThing):
|
||||
bindingHeaders["WrapperFactory.h"] = descriptors
|
||||
bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
|
||||
bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
|
||||
bindingHeaders["xpcpublic.h"] = dictionaries ## xpc::GetSafeJSContextGlobal
|
||||
bindingHeaders["xpcpublic.h"] = dictionaries ## xpc::UnprivilegedJunkScope
|
||||
|
||||
# Do codegen for all the dictionaries. We have to be a bit careful
|
||||
# here, because we have to generate these in order from least derived
|
||||
|
@ -495,7 +495,7 @@ SmsRequestParent::DoRequest(const SendMessageRequest& aRequest)
|
||||
// jsval to ::Send. Only system code should be looking at the result here,
|
||||
// so we just create it in the System-Principaled Junk Scope.
|
||||
AutoJSContext cx;
|
||||
JSAutoCompartment ac(cx, xpc::GetJunkScope());
|
||||
JSAutoCompartment ac(cx, xpc::PrivilegedJunkScope());
|
||||
JS::Rooted<JS::Value> params(cx);
|
||||
const SendMmsMessageRequest &req = aRequest.get_SendMmsMessageRequest();
|
||||
if (!GetParamsFromSendMmsMessageRequest(cx,
|
||||
|
@ -13,6 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=667388
|
||||
<![CDATA[
|
||||
|
||||
// Setup.
|
||||
const Cu = Components.utils;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.testObject = { myNumber: 42,
|
||||
myDomain: window.location.domain };
|
||||
@ -25,34 +26,34 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=667388
|
||||
// Mark this frame as loaded.
|
||||
framesLoaded[id] = true;
|
||||
|
||||
// Allow it to call |is|.
|
||||
// Allow it to call various helpers.
|
||||
window.frames[id].wrappedJSObject.is = is;
|
||||
window.frames[id].wrappedJSObject.ok = ok;
|
||||
window.frames[id].wrappedJSObject.info = info;
|
||||
|
||||
// If all the frames are loaded, start the test.
|
||||
if (framesLoaded[1] && framesLoaded[2] && framesLoaded[3])
|
||||
startTest();
|
||||
}
|
||||
|
||||
function reject(e) {
|
||||
ok(false, "Rejected Promise: " + e);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
|
||||
// Test interaction between chrome and content.
|
||||
runChromeContentTest(window.frames[1]);
|
||||
|
||||
// Try same origin.
|
||||
runContentContentTest(window.frames[1], window.frames[2],
|
||||
true, "Should be able to clone same-origin");
|
||||
|
||||
// Try cross-origin.
|
||||
runContentContentTest(window.frames[2], window.frames[3],
|
||||
false, "Should not be able to clone cross-origin");
|
||||
|
||||
// Colaborate with document.domain, then try again.
|
||||
frames[2].document.domain = 'example.org';
|
||||
frames[3].document.domain = 'example.org';
|
||||
runContentContentTest(window.frames[2], window.frames[3],
|
||||
true, "Should be able to clone cross-origin with document.domain")
|
||||
|
||||
SimpleTest.finish();
|
||||
runChromeContentTest(window.frames[1]).then(
|
||||
runContentContentTest.bind(null, window.frames[1], window.frames[2],
|
||||
true, "Should be able to clone same-origin"), reject).then(
|
||||
runContentContentTest.bind(null, window.frames[2], window.frames[3],
|
||||
false, "Should not be able to clone cross-origin"), reject).then(function() {
|
||||
// Colaborate with document.domain, then try again.
|
||||
frames[2].document.domain = 'example.org';
|
||||
frames[3].document.domain = 'example.org';
|
||||
return runContentContentTest(window.frames[2], window.frames[3],
|
||||
true, "Should be able to clone cross-origin with document.domain")
|
||||
}, reject).then(SimpleTest.finish.bind(SimpleTest), reject);
|
||||
}
|
||||
|
||||
// Tests cloning between chrome and content.
|
||||
@ -63,12 +64,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=667388
|
||||
true,
|
||||
"Chrome should be able to clone content object");
|
||||
|
||||
// Content should not be able to clone a chrome object.
|
||||
//
|
||||
// Note that because we're using |wrappedJSObject| here, the subject
|
||||
// principal is clamed to the principal of the iframe, which is what we want.
|
||||
contentWin.wrappedJSObject.tryToClone(window.testObject, false,
|
||||
"Content should not be able to clone chrome object");
|
||||
// COWs without __exposedProps__ silently deny property gets. So Content
|
||||
// should be able to clone a COW, but the result of the clone should be empty.
|
||||
var p = contentWin.wrappedJSObject.tryToClone(window.testObject, true,
|
||||
"Content should be able to clone COW");
|
||||
return new Promise(function(resolve) {
|
||||
p.then(function(cloneResult) {
|
||||
is(Cu.waiveXrays(cloneResult).toSource(), "({})", "Empty COW clone");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Test cloning between content and content.
|
||||
@ -81,24 +86,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=667388
|
||||
// long as the security wrappers check documentDomainMakesSameOrigin directly
|
||||
// for the puncture case rather than checking IsTransparent(), but it still
|
||||
// gives rise to a situation that wouldn't really happen in practice.
|
||||
//
|
||||
// A more serious issues is that we don't/can't test the Xray wrapper path,
|
||||
// because the only native objects that we successfully send through
|
||||
// structured clone don't force a parent in PreCreate, and so we _can't_
|
||||
// have security wrappers around them (we get a new XPCWN in each
|
||||
// compartment). Since the case can't happen, we can't write tests for it.
|
||||
// But when it can happen (ie, we fix PreCreate for File, FileList, Blob, etc),
|
||||
// we want tests right away, because this stuff is very security-sensitive.
|
||||
// So we set this test up to fail when things are fixed, so that the someone
|
||||
// will notice and properly augment this test to cover native objects.
|
||||
function runContentContentTest(win1, win2, shouldSucceed, msg) {
|
||||
win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.testObject,
|
||||
shouldSucceed, msg);
|
||||
|
||||
var fixMsg = "If this fails, you fixed PreCreate. That's a good thing, ";
|
||||
fixMsg += "but now we need some test coverage. See the above comment.";
|
||||
win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.blob, true, fixMsg);
|
||||
win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.filelist, true, fixMsg);
|
||||
var p = win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.testObject,
|
||||
shouldSucceed, msg);
|
||||
if (!shouldSucceed)
|
||||
return;
|
||||
return new Promise(function(resolve) {
|
||||
p.then(function(cloneResult) {
|
||||
is(Cu.waiveXrays(cloneResult).toSource(), win2.wrappedJSObject.testObject.toSource(),
|
||||
"Clone should create an identical object");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function tryToClone(obj, shouldSucceed, message) {
|
||||
|
@ -3,27 +3,34 @@
|
||||
<head>
|
||||
<script type="application/javascript">
|
||||
|
||||
function waitForMessage() {
|
||||
return new Promise(function(resolve) {
|
||||
window.addEventListener('message', function l(evt) {
|
||||
window.removeEventListener('message', l);
|
||||
resolve(evt.data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Set up the objects for cloning.
|
||||
function setup() {
|
||||
window.testObject = { myNumber: 42,
|
||||
myDomain: window.location.domain };
|
||||
window.blob = new Blob([], { type: 'text/plain' });
|
||||
window.fileList = document.getElementById('fileinput').files;
|
||||
myString: "hello",
|
||||
myImageData: new ImageData(10, 10) };
|
||||
}
|
||||
|
||||
// Called by the chrome parent window.
|
||||
function tryToClone(obj, shouldSucceed, message) {
|
||||
var success = false;
|
||||
var sink = window.frames[0];
|
||||
try { sink.postMessage(obj, '*'); success = true; }
|
||||
try { window.postMessage(obj, '*'); success = true; }
|
||||
catch (e) { message = message + ' (threw: ' + e.message + ')'; }
|
||||
is(success, shouldSucceed, message);
|
||||
return (success && shouldSucceed) ? waitForMessage() : Promise.resolve();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="setup()">
|
||||
<input id="fileinput" type="file"></input>
|
||||
<iframe id="sink">
|
||||
</body>
|
||||
</html>
|
||||
|
@ -143,7 +143,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
|
||||
parent.addEventListener("click", parentClickTest);
|
||||
child.addEventListener("click", childClickTest);
|
||||
SimpleTest.executeSoon(function () {
|
||||
synthesizeMouseAtCenter(child, {type: "click"}, window);
|
||||
synthesizeMouseAtCenter(child, {}, window);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -35,3 +35,6 @@ interface AnalyserNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
AnalyserNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -48,4 +48,9 @@ partial interface AudioNode {
|
||||
[ChromeOnly]
|
||||
readonly attribute unsigned long id;
|
||||
};
|
||||
[NoInterfaceObject]
|
||||
interface AudioNodePassThrough {
|
||||
[ChromeOnly]
|
||||
attribute boolean passThrough;
|
||||
};
|
||||
|
||||
|
@ -35,3 +35,6 @@ interface BiquadFilterNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
BiquadFilterNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -18,3 +18,6 @@ interface ConvolverNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
ConvolverNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -16,3 +16,6 @@ interface DelayNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
DelayNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -21,3 +21,6 @@ interface DynamicsCompressorNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
DynamicsCompressorNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -16,3 +16,6 @@ interface GainNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
GainNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -44,3 +44,6 @@ interface PannerNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
PannerNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -18,3 +18,6 @@ interface ScriptProcessorNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
ScriptProcessorNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -23,3 +23,6 @@ interface WaveShaperNode : AudioNode {
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
WaveShaperNode implements AudioNodePassThrough;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
@ -60,10 +61,9 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
|
||||
{
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -83,10 +83,9 @@ ServiceWorkerContainer::Unregister(const nsAString& aScope,
|
||||
{
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -138,8 +137,8 @@ ServiceWorkerContainer::GetController()
|
||||
{
|
||||
if (!mControllerWorker) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -177,7 +176,7 @@ ServiceWorkerContainer::GetReady(ErrorResult& aRv)
|
||||
void
|
||||
ServiceWorkerContainer::StartListeningForEvents()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->AddContainerEventListener(mWindow->GetDocumentURI(), this);
|
||||
}
|
||||
@ -186,7 +185,7 @@ ServiceWorkerContainer::StartListeningForEvents()
|
||||
void
|
||||
ServiceWorkerContainer::StopListeningForEvents()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->RemoveContainerEventListener(mWindow->GetDocumentURI(), this);
|
||||
}
|
||||
@ -212,8 +211,8 @@ already_AddRefed<workers::ServiceWorker>
|
||||
ServiceWorkerContainer::GetWorkerReference(WhichServiceWorker aWhichOne)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -254,10 +253,9 @@ ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl,
|
||||
nsString& aScope,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -607,8 +607,7 @@ ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
already_AddRefed<ServiceWorkerManager>
|
||||
ServiceWorkerManager::GetInstance()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||
do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
||||
return concrete.forget();
|
||||
}
|
||||
|
@ -359,7 +359,6 @@ void
|
||||
AssertInCompilationScope()
|
||||
{
|
||||
AutoJSContext cx;
|
||||
// Note - Inverting the order of these operands is a rooting hazard.
|
||||
MOZ_ASSERT(xpc::GetCompilationScope() == JS::CurrentGlobalOrNull(cx));
|
||||
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
|
||||
}
|
||||
#endif
|
||||
|
@ -240,8 +240,7 @@ nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding)
|
||||
// bind the prototype to a real xbl instance, we'll clone the pre-compiled JS into the real instance's
|
||||
// context.
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
|
||||
NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::CompilationScope());
|
||||
JSAutoCompartment ac(cx, compilationGlobal);
|
||||
|
||||
mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), compilationGlobal);
|
||||
|
@ -911,8 +911,7 @@ nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
|
||||
}
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
|
||||
NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::CompilationScope());
|
||||
JSAutoCompartment ac(cx, compilationGlobal);
|
||||
|
||||
bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
|
||||
@ -1064,8 +1063,7 @@ nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
|
||||
// computed on demand.
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
|
||||
NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::CompilationScope());
|
||||
JSAutoCompartment ac(cx, compilationGlobal);
|
||||
|
||||
uint8_t flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
|
||||
|
@ -2,8 +2,6 @@
|
||||
# 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/.
|
||||
|
||||
include libeditor/html/crashtests/crashtests.list
|
||||
include libeditor/crashtests/crashtests.list
|
||||
include libeditor/text/crashtests/crashtests.list
|
||||
include composer/crashtests/crashtests.list
|
||||
include txmgr/tests/crashtests/crashtests.list
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user