mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
d2da7791d3
2
CLOBBER
2
CLOBBER
@ -18,4 +18,4 @@
|
||||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Bug 918207 needed a clobber on every platform because we can't have nice things
|
||||
Bug 899574 needed a clobber on every platform because we can't have nice things
|
||||
|
@ -11,6 +11,7 @@ Cu.import('resource://gre/modules/AlarmService.jsm');
|
||||
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
||||
Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
|
||||
Cu.import('resource://gre/modules/ObjectWrapper.jsm');
|
||||
Cu.import('resource://gre/modules/NotificationDB.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/AccessFu.jsm');
|
||||
Cu.import('resource://gre/modules/Payment.jsm');
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "a57a913f1dd723afa191124f27b8d9fc4b0cb1c0",
|
||||
"revision": "20e3f42ccb6073c6d9bc9741de3a19a939a8a7d8",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -352,6 +352,8 @@
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
@BINPATH@/components/PhoneNumberService.js
|
||||
@BINPATH@/components/PhoneNumberService.manifest
|
||||
@BINPATH@/components/NotificationStorage.js
|
||||
@BINPATH@/components/NotificationStorage.manifest
|
||||
@BINPATH@/components/PermissionSettings.js
|
||||
@BINPATH@/components/PermissionSettings.manifest
|
||||
@BINPATH@/components/PermissionPromptService.js
|
||||
@ -543,9 +545,6 @@
|
||||
@BINPATH@/components/TCPSocketParentIntermediary.js
|
||||
@BINPATH@/components/TCPSocket.manifest
|
||||
|
||||
@BINPATH@/components/AppProtocolHandler.js
|
||||
@BINPATH@/components/AppProtocolHandler.manifest
|
||||
|
||||
@BINPATH@/components/Payment.js
|
||||
@BINPATH@/components/PaymentFlowInfo.js
|
||||
@BINPATH@/components/PaymentRequestInfo.js
|
||||
|
@ -365,12 +365,6 @@ pref("browser.download.manager.quitBehavior", 0);
|
||||
pref("browser.download.manager.scanWhenDone", true);
|
||||
pref("browser.download.manager.resumeOnWakeDelay", 10000);
|
||||
|
||||
// Enables the asynchronous Downloads API in the Downloads Panel.
|
||||
pref("browser.download.useJSTransfer", true);
|
||||
|
||||
// This allows disabling the Downloads Panel in favor of the old interface.
|
||||
pref("browser.download.useToolkitUI", false);
|
||||
|
||||
// This allows disabling the animated notifications shown by
|
||||
// the Downloads Indicator when a download starts or completes.
|
||||
pref("browser.download.animateNotifications", true);
|
||||
|
@ -7,6 +7,7 @@ let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NotificationDB.jsm");
|
||||
Cu.import("resource:///modules/RecentWindow.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
|
@ -235,15 +235,12 @@ this.DownloadsCommon = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates whether we should show the full Download Manager window interface
|
||||
* instead of the simplified panel interface. The behavior of downloads
|
||||
* across browsing session is consistent with the selected interface.
|
||||
* Indicates that we should show the simplified panel interface instead of the
|
||||
* full Download Manager window interface. The code associated with the
|
||||
* Download Manager window interface will be removed in bug 899110.
|
||||
*/
|
||||
get useToolkitUI()
|
||||
{
|
||||
try {
|
||||
return Services.prefs.getBoolPref("browser.download.useToolkitUI");
|
||||
} catch (ex) { }
|
||||
return false;
|
||||
},
|
||||
|
||||
@ -570,17 +567,12 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns true if we should hook the panel to the JavaScript API for downloads
|
||||
* instead of the nsIDownloadManager back-end. In order for the logic to work
|
||||
* properly, this value never changes during the execution of the application,
|
||||
* even if the underlying preference value has changed. A restart is required
|
||||
* for the change to take effect.
|
||||
* Returns true to indicate that we should hook the panel to the JavaScript API
|
||||
* for downloads instead of the nsIDownloadManager back-end. The code
|
||||
* associated with nsIDownloadManager will be removed in bug 899110.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(DownloadsCommon, "useJSTransfer", function () {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("browser.download.useJSTransfer");
|
||||
} catch (ex) { }
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -93,29 +93,13 @@ DownloadsStartup.prototype = {
|
||||
.registerFactory(kDownloadsUICid, "",
|
||||
kDownloadsUIContractId, null);
|
||||
|
||||
// If the integration preference is enabled, override Toolkit's
|
||||
// nsITransfer implementation with the one from the JavaScript API for
|
||||
// downloads. This should be used only by developers while testing new
|
||||
// code that uses the JavaScript API, and will eventually be removed
|
||||
// when nsIDownloadManager will not be available anymore (bug 851471).
|
||||
let useJSTransfer = false;
|
||||
try {
|
||||
// For performance reasons, we don't want to load the DownloadsCommon
|
||||
// module during startup, so we read the preference value directly.
|
||||
useJSTransfer =
|
||||
Services.prefs.getBoolPref("browser.download.useJSTransfer");
|
||||
} catch (ex) { }
|
||||
if (useJSTransfer) {
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(kTransferCid, "",
|
||||
kTransferContractId, null);
|
||||
} else {
|
||||
// The other notifications are handled internally by the JavaScript
|
||||
// API for downloads, no need to observe when that API is enabled.
|
||||
for (let topic of kObservedTopics) {
|
||||
Services.obs.addObserver(this, topic, true);
|
||||
}
|
||||
}
|
||||
// Override Toolkit's nsITransfer implementation with the one from the
|
||||
// JavaScript API for downloads. This will eventually be removed when
|
||||
// nsIDownloadManager will not be available anymore (bug 851471). The
|
||||
// old code in this module will be removed in bug 899110.
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(kTransferCid, "",
|
||||
kTransferContractId, null);
|
||||
break;
|
||||
|
||||
case "sessionstore-windows-restored":
|
||||
|
@ -1420,9 +1420,6 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
this._setPersist(toolbarResource, currentsetResource, currentset);
|
||||
}
|
||||
|
||||
Services.prefs.clearUserPref("browser.download.useToolkitUI");
|
||||
Services.prefs.clearUserPref("browser.library.useNewDownloadsView");
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -350,6 +350,10 @@ let SessionStoreInternal = {
|
||||
// See bug 516755.
|
||||
_disabledForMultiProcess: false,
|
||||
|
||||
// Promise that is resolved when we're ready to initialize
|
||||
// and restore the session.
|
||||
_promiseReadyForInitialization: null,
|
||||
|
||||
/**
|
||||
* A promise fulfilled once initialization is complete.
|
||||
*/
|
||||
@ -870,13 +874,33 @@ let SessionStoreInternal = {
|
||||
return;
|
||||
}
|
||||
|
||||
// The very first window that is opened creates a promise that is then
|
||||
// re-used by all subsequent windows. The promise will be used to tell
|
||||
// when we're ready for initialization.
|
||||
if (!this._promiseReadyForInitialization) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Wait for the given window's delayed startup to be finished.
|
||||
Services.obs.addObserver(function obs(subject, topic) {
|
||||
if (aWindow == subject) {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
|
||||
// We are ready for initialization as soon as the session file has been
|
||||
// read from disk and the initial window's delayed startup has finished.
|
||||
this._promiseReadyForInitialization =
|
||||
Promise.all([deferred.promise, gSessionStartup.onceInitialized]);
|
||||
}
|
||||
|
||||
// We can't call this.onLoad since initialization
|
||||
// hasn't completed, so we'll wait until it is done.
|
||||
// Even if additional windows are opened and wait
|
||||
// for initialization as well, the first opened
|
||||
// window should execute first, and this.onLoad
|
||||
// will be called with the initialState.
|
||||
gSessionStartup.onceInitialized.then(() => {
|
||||
this._promiseReadyForInitialization.then(() => {
|
||||
if (aWindow.closed) {
|
||||
return;
|
||||
}
|
||||
|
@ -61,3 +61,5 @@ fi
|
||||
MOZ_WEBGL_CONFORMANT=1
|
||||
# Enable navigator.mozPay
|
||||
MOZ_PAY=1
|
||||
MOZ_JSDOWNLOADS=1
|
||||
|
||||
|
@ -1156,8 +1156,13 @@ SourceScripts.prototype = {
|
||||
_onBlackBoxChange: function (aEvent, { url, isBlackBoxed }) {
|
||||
const item = DebuggerView.Sources.getItemByValue(url);
|
||||
if (item) {
|
||||
DebuggerView.Sources.callMethod("checkItem", item.target, !isBlackBoxed);
|
||||
if (isBlackBoxed) {
|
||||
item.target.classList.add("black-boxed");
|
||||
} else {
|
||||
item.target.classList.remove("black-boxed");
|
||||
}
|
||||
}
|
||||
DebuggerView.Sources.updateToolbarButtonsState();
|
||||
DebuggerView.maybeShowBlackBoxMessage();
|
||||
},
|
||||
|
||||
|
@ -19,7 +19,7 @@ function SourcesView() {
|
||||
this._onSourceSelect = this._onSourceSelect.bind(this);
|
||||
this._onSourceClick = this._onSourceClick.bind(this);
|
||||
this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
|
||||
this._onSourceCheck = this._onSourceCheck.bind(this);
|
||||
this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
|
||||
this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
|
||||
this._onBreakpointClick = this._onBreakpointClick.bind(this);
|
||||
this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this);
|
||||
@ -28,7 +28,7 @@ function SourcesView() {
|
||||
this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
|
||||
this._onConditionalTextboxInput = this._onConditionalTextboxInput.bind(this);
|
||||
this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
|
||||
this._updatePrettyPrintButtonState = this._updatePrettyPrintButtonState.bind(this);
|
||||
this.updateToolbarButtonsState = this.updateToolbarButtonsState.bind(this);
|
||||
}
|
||||
|
||||
SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
@ -39,7 +39,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
dumpn("Initializing the SourcesView");
|
||||
|
||||
this.widget = new SideMenuWidget(document.getElementById("sources"), {
|
||||
showItemCheckboxes: true,
|
||||
showArrows: true
|
||||
});
|
||||
|
||||
@ -51,6 +50,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this._cmPopup = document.getElementById("sourceEditorContextMenu");
|
||||
this._cbPanel = document.getElementById("conditional-breakpoint-panel");
|
||||
this._cbTextbox = document.getElementById("conditional-breakpoint-panel-textbox");
|
||||
this._blackBoxButton = document.getElementById("black-box");
|
||||
this._stopBlackBoxButton = document.getElementById("black-boxed-message-button");
|
||||
this._prettyPrintButton = document.getElementById("pretty-print");
|
||||
|
||||
@ -62,7 +62,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
window.on(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
|
||||
this.widget.addEventListener("select", this._onSourceSelect, false);
|
||||
this.widget.addEventListener("click", this._onSourceClick, false);
|
||||
this.widget.addEventListener("check", this._onSourceCheck, false);
|
||||
this._stopBlackBoxButton.addEventListener("click", this._onStopBlackBoxing, false);
|
||||
this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
|
||||
this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
|
||||
@ -86,7 +85,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
window.off(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
|
||||
this.widget.removeEventListener("select", this._onSourceSelect, false);
|
||||
this.widget.removeEventListener("click", this._onSourceClick, false);
|
||||
this.widget.removeEventListener("check", this._onSourceCheck, false);
|
||||
this._stopBlackBoxButton.removeEventListener("click", this._onStopBlackBoxing, false);
|
||||
this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing, false);
|
||||
this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShown, false);
|
||||
@ -702,22 +700,23 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
document.title = L10N.getFormatStr("DebuggerWindowScriptTitle", script);
|
||||
|
||||
DebuggerView.maybeShowBlackBoxMessage();
|
||||
this._updatePrettyPrintButtonState();
|
||||
this.updateToolbarButtonsState();
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable or disable the pretty print button depending on whether the selected
|
||||
* source is black boxed or not and check or uncheck it depending on if the
|
||||
* selected source is already pretty printed or not.
|
||||
* Update the checked/unchecked and enabled/disabled states of the buttons in
|
||||
* the sources toolbar based on the currently selected source's state.
|
||||
*/
|
||||
_updatePrettyPrintButtonState: function() {
|
||||
updateToolbarButtonsState: function() {
|
||||
const { source } = this.selectedItem.attachment;
|
||||
const sourceClient = gThreadClient.source(source);
|
||||
|
||||
if (sourceClient.isBlackBoxed) {
|
||||
this._prettyPrintButton.setAttribute("disabled", true);
|
||||
this._blackBoxButton.setAttribute("checked", true);
|
||||
} else {
|
||||
this._prettyPrintButton.removeAttribute("disabled");
|
||||
this._blackBoxButton.removeAttribute("checked");
|
||||
}
|
||||
|
||||
if (sourceClient.isPrettyPrinted) {
|
||||
@ -736,26 +735,30 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
},
|
||||
|
||||
/**
|
||||
* The check listener for the sources container.
|
||||
* Toggle the black boxed state of the selected source.
|
||||
*/
|
||||
_onSourceCheck: function({ detail: { checked }, target }) {
|
||||
const shouldBlackBox = !checked;
|
||||
toggleBlackBoxing: function() {
|
||||
const { source } = this.selectedItem.attachment;
|
||||
const sourceClient = gThreadClient.source(source);
|
||||
const shouldBlackBox = !sourceClient.isBlackBoxed;
|
||||
|
||||
// Be optimistic that the (un-)black boxing will succeed and enable/disable
|
||||
// the pretty print button immediately. Then, once we actually get the
|
||||
// results from the server, make sure that it is in the correct state again
|
||||
// by calling `_updatePrettyPrintButtonState`.
|
||||
// Be optimistic that the (un-)black boxing will succeed, so enable/disable
|
||||
// the pretty print button and check/uncheck the black box button
|
||||
// immediately. Then, once we actually get the results from the server, make
|
||||
// sure that it is in the correct state again by calling
|
||||
// `updateToolbarButtonsState`.
|
||||
|
||||
if (shouldBlackBox) {
|
||||
this._prettyPrintButton.setAttribute("disabled", true);
|
||||
this._blackBoxButton.setAttribute("checked", true);
|
||||
} else {
|
||||
this._prettyPrintButton.removeAttribute("disabled");
|
||||
this._blackBoxButton.removeAttribute("checked");
|
||||
}
|
||||
|
||||
const { source } = this.getItemForElement(target).attachment;
|
||||
DebuggerController.SourceScripts.blackBox(source, shouldBlackBox)
|
||||
.then(this._updatePrettyPrintButtonState,
|
||||
this._updatePrettyPrintButtonState);
|
||||
.then(this.updateToolbarButtonsState,
|
||||
this.updateToolbarButtonsState);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -763,7 +766,9 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*/
|
||||
_onStopBlackBoxing: function() {
|
||||
let sourceForm = this.selectedItem.attachment.source;
|
||||
DebuggerController.SourceScripts.blackBox(sourceForm, false);
|
||||
DebuggerController.SourceScripts.blackBox(sourceForm, false)
|
||||
.then(this.updateToolbarButtonsState,
|
||||
this.updateToolbarButtonsState);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,8 @@
|
||||
<commandset id="debuggerCommands">
|
||||
<command id="prettyPrintCommand"
|
||||
oncommand="DebuggerView.Sources.togglePrettyPrint()"/>
|
||||
<command id="blackBoxCommand"
|
||||
oncommand="DebuggerView.Sources.toggleBlackBoxing()"/>
|
||||
<command id="unBlackBoxButton"
|
||||
oncommand="DebuggerView.Sources._onStopBlackBoxing()"/>
|
||||
<command id="nextSourceCommand"
|
||||
@ -320,12 +322,18 @@
|
||||
<vbox id="sources-pane">
|
||||
<vbox id="sources" flex="1"/>
|
||||
<toolbar id="sources-toolbar" class="devtools-toolbar">
|
||||
<toolbarbutton id="pretty-print"
|
||||
label="{}"
|
||||
tooltiptext="&debuggerUI.sources.prettyPrint;"
|
||||
class="devtools-toolbarbutton devtools-monospace"
|
||||
command="prettyPrintCommand"
|
||||
hidden="true"/>
|
||||
<hbox id="sources-controls">
|
||||
<toolbarbutton id="black-box"
|
||||
tooltiptext="&debuggerUI.sources.blackBoxTooltip;"
|
||||
command="blackBoxCommand"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<toolbarbutton id="pretty-print"
|
||||
label="{}"
|
||||
tooltiptext="&debuggerUI.sources.prettyPrint;"
|
||||
class="devtools-toolbarbutton devtools-monospace"
|
||||
command="prettyPrintCommand"
|
||||
hidden="true"/>
|
||||
</hbox>
|
||||
</toolbar>
|
||||
</vbox>
|
||||
<splitter id="sources-and-editor-splitter"
|
||||
|
@ -65,7 +65,6 @@ support-files =
|
||||
[browser_dbg_blackboxing-04.js]
|
||||
[browser_dbg_blackboxing-05.js]
|
||||
[browser_dbg_blackboxing-06.js]
|
||||
[browser_dbg_blackboxing-07.js]
|
||||
[browser_dbg_file-reload.js]
|
||||
[browser_dbg_breadcrumbs-access.js]
|
||||
[browser_dbg_break-on-dom-01.js]
|
||||
|
@ -27,24 +27,19 @@ function test() {
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
const checkbox = gDebugger.document.querySelector(".side-menu-widget-item-checkbox");
|
||||
ok(checkbox, "Should get the checkbox for black boxing the source.");
|
||||
ok(checkbox.checked, "Should not be black boxed by default.");
|
||||
const bbButton = getBlackBoxButton(gPanel);
|
||||
ok(!bbButton.checked, "Should not be black boxed by default");
|
||||
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange").then(aSource => {
|
||||
return toggleBlackBoxing(gPanel).then(aSource => {
|
||||
ok(aSource.isBlackBoxed, "The source should be black boxed now.");
|
||||
ok(!checkbox.checked, "The checkbox should no longer be checked.");
|
||||
ok(bbButton.checked, "The checkbox should no longer be checked.");
|
||||
});
|
||||
|
||||
checkbox.click();
|
||||
return finished;
|
||||
}
|
||||
|
||||
function testBlackBoxReload() {
|
||||
return reloadActiveTab(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(() => {
|
||||
const checkbox = gDebugger.document.querySelector(".side-menu-widget-item-checkbox");
|
||||
ok(checkbox, "Should get the checkbox for black boxing the source.");
|
||||
ok(!checkbox.checked, "Should still be black boxed.");
|
||||
const bbButton = getBlackBoxButton(gPanel);
|
||||
ok(bbButton.checked, "Should still be black boxed.");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,9 @@ function test() {
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange").then(aSource => {
|
||||
return toggleBlackBoxing(gPanel).then(aSource => {
|
||||
ok(aSource.isBlackBoxed, "The source should be black boxed now.");
|
||||
});
|
||||
|
||||
getBlackBoxCheckbox(BLACKBOXME_URL).click();
|
||||
return finished;
|
||||
}
|
||||
|
||||
function testBlackBoxStack() {
|
||||
@ -53,12 +50,6 @@ function testBlackBoxStack() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(aUrl) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\"" + aUrl + "\"] " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
|
@ -40,7 +40,7 @@ function testBlackBoxStack() {
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange").then(aSource => {
|
||||
return toggleBlackBoxing(gPanel, BLACKBOXME_URL).then(aSource => {
|
||||
ok(aSource.isBlackBoxed, "The source should be black boxed now.");
|
||||
|
||||
is(gFrames.itemCount, 3,
|
||||
@ -48,15 +48,6 @@ function testBlackBoxSource() {
|
||||
is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
|
||||
"And one of them should be the combined black boxed frames.");
|
||||
});
|
||||
|
||||
getBlackBoxCheckbox(BLACKBOXME_URL).click();
|
||||
return finished;
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(aUrl) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\"" + aUrl + "\"] " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
|
@ -32,9 +32,9 @@ function test() {
|
||||
|
||||
function blackBoxSources() {
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange", 3);
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "code_blackboxing_one.js").click();
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "code_blackboxing_two.js").click();
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "code_blackboxing_three.js").click();
|
||||
toggleBlackBoxing(gPanel, EXAMPLE_URL + "code_blackboxing_one.js");
|
||||
toggleBlackBoxing(gPanel, EXAMPLE_URL + "code_blackboxing_two.js");
|
||||
toggleBlackBoxing(gPanel, EXAMPLE_URL + "code_blackboxing_three.js");
|
||||
return finished;
|
||||
}
|
||||
|
||||
@ -52,12 +52,6 @@ function testBlackBoxStack() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(aUrl) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\"" + aUrl + "\"] " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
|
@ -21,7 +21,7 @@ function test() {
|
||||
|
||||
waitForSourceShown(gPanel, ".coffee")
|
||||
.then(testSourceEditorShown)
|
||||
.then(blackBoxSource)
|
||||
.then(toggleBlackBoxing.bind(null, gPanel))
|
||||
.then(testBlackBoxMessageShown)
|
||||
.then(clickStopBlackBoxingButton)
|
||||
.then(testSourceEditorShownAgain)
|
||||
@ -37,12 +37,6 @@ function testSourceEditorShown() {
|
||||
"The first item in the deck should be selected (the source editor).");
|
||||
}
|
||||
|
||||
function blackBoxSource() {
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange");
|
||||
getAnyBlackBoxCheckbox().click();
|
||||
return finished;
|
||||
}
|
||||
|
||||
function testBlackBoxMessageShown() {
|
||||
is(gDeck.selectedIndex, "1",
|
||||
"The second item in the deck should be selected (the black box message).");
|
||||
@ -59,11 +53,6 @@ function testSourceEditorShownAgain() {
|
||||
"The first item in the deck should be selected again (the source editor).");
|
||||
}
|
||||
|
||||
function getAnyBlackBoxCheckbox() {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item .side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
function getEditorBlackboxMessageButton() {
|
||||
return gDebugger.document.getElementById("black-boxed-message-button");
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that clicking the black box checkbox doesn't select that source.
|
||||
* Test that clicking the black box checkbox when paused doesn't re-select the
|
||||
* currently paused frame's source.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
@ -18,35 +19,35 @@ function test() {
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
waitForSourceShown(gPanel, ".js")
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
|
||||
.then(testBlackBox)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBox() {
|
||||
const selectedUrl = gSources.selectedValue;
|
||||
const checkbox = getDifferentBlackBoxCheckbox(selectedUrl);
|
||||
ok(checkbox, "We should be able to grab a different checkbox.");
|
||||
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange").then(() => {
|
||||
is(selectedUrl, gSources.selectedValue,
|
||||
"The same source should still be selected.");
|
||||
let finished = waitForSourceShown(gPanel, "blackboxme.js").then(() => {
|
||||
const newSelectedUrl = gSources.selectedValue;
|
||||
isnot(selectedUrl, newSelectedUrl,
|
||||
"Should not have the same url selected.");
|
||||
|
||||
return toggleBlackBoxing(gPanel).then(() => {
|
||||
is(gSources.selectedValue, newSelectedUrl,
|
||||
"The selected source did not change.");
|
||||
});
|
||||
});
|
||||
|
||||
checkbox.click();
|
||||
gSources.selectedIndex = 0;
|
||||
return finished;
|
||||
}
|
||||
|
||||
function getDifferentBlackBoxCheckbox(aUrl) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item:not([tooltiptext=\"" + aUrl + "\"]) " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
|
@ -1,66 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that clicking the black box checkbox when paused doesn't re-select the
|
||||
* currently paused frame's source.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gSources;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
|
||||
.then(testBlackBox)
|
||||
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBox() {
|
||||
const selectedUrl = gSources.selectedValue;
|
||||
|
||||
let finished = waitForSourceShown(gPanel, "blackboxme.js").then(() => {
|
||||
const newSelectedUrl = gSources.selectedValue;
|
||||
isnot(selectedUrl, newSelectedUrl,
|
||||
"Should not have the same url selected.");
|
||||
|
||||
let finished = waitForThreadEvents(gPanel, "blackboxchange").then(() => {
|
||||
is(gSources.selectedValue, newSelectedUrl,
|
||||
"The selected source did not change.");
|
||||
});
|
||||
|
||||
getBlackBoxCheckbox(newSelectedUrl).click()
|
||||
return finished;
|
||||
});
|
||||
|
||||
gSources.selectedIndex = 0;
|
||||
return finished;
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(aUrl) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\"" + aUrl + "\"] " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gSources = null;
|
||||
});
|
@ -46,69 +46,95 @@ function waitForDebuggerSources() {
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
return cmd("dbg blackbox " + BLACKBOXME_URL).then(() => {
|
||||
const checkbox = getBlackBoxCheckbox(BLACKBOXME_URL);
|
||||
ok(!checkbox.checked,
|
||||
"Should be able to black box a specific source.");
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg blackbox " + BLACKBOXME_URL);
|
||||
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXME_URL);
|
||||
ok(bbButton.checked,
|
||||
"Should be able to black box a specific source.");
|
||||
});
|
||||
}
|
||||
|
||||
function testUnBlackBoxSource() {
|
||||
return cmd("dbg unblackbox " + BLACKBOXME_URL).then(() => {
|
||||
const checkbox = getBlackBoxCheckbox(BLACKBOXME_URL);
|
||||
ok(checkbox.checked,
|
||||
"Should be able to stop black boxing a specific source.");
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg unblackbox " + BLACKBOXME_URL);
|
||||
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXME_URL);
|
||||
ok(!bbButton.checked,
|
||||
"Should be able to stop black boxing a specific source.");
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxGlob() {
|
||||
return cmd("dbg blackbox --glob *blackboxing_t*.js", 2,
|
||||
[/blackboxing_three\.js/g, /blackboxing_two\.js/g]).then(() => {
|
||||
ok(getBlackBoxCheckbox(BLACKBOXME_URL).checked,
|
||||
"blackboxme should not be black boxed because it doesn't match the glob.");
|
||||
ok(getBlackBoxCheckbox(BLACKBOXONE_URL).checked,
|
||||
"blackbox_one should not be black boxed because it doesn't match the glob.");
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg blackbox --glob *blackboxing_t*.js", 2,
|
||||
[/blackboxing_three\.js/g, /blackboxing_two\.js/g]);
|
||||
|
||||
ok(!getBlackBoxCheckbox(BLACKBOXTWO_URL).checked,
|
||||
"blackbox_two should be black boxed because it matches the glob.");
|
||||
ok(!getBlackBoxCheckbox(BLACKBOXTHREE_URL).checked,
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXME_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackboxme should not be black boxed because it doesn't match the glob.");
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXONE_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_one should not be black boxed because it doesn't match the glob.");
|
||||
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTWO_URL);
|
||||
ok(bbButton.checked,
|
||||
"blackbox_two should be black boxed because it matches the glob.");
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTHREE_URL);
|
||||
ok(bbButton.checked,
|
||||
"blackbox_three should be black boxed because it matches the glob.");
|
||||
});
|
||||
}
|
||||
|
||||
function testUnBlackBoxGlob() {
|
||||
return cmd("dbg unblackbox --glob *blackboxing_t*.js", 2).then(() => {
|
||||
ok(getBlackBoxCheckbox(BLACKBOXTWO_URL).checked,
|
||||
"blackbox_two should be un-black boxed because it matches the glob.");
|
||||
ok(getBlackBoxCheckbox(BLACKBOXTHREE_URL).checked,
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg unblackbox --glob *blackboxing_t*.js", 2);
|
||||
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTWO_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_two should be un-black boxed because it matches the glob.");
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTHREE_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_three should be un-black boxed because it matches the glob.");
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxInvert() {
|
||||
return cmd("dbg blackbox --invert --glob *blackboxing_t*.js", 3,
|
||||
[/blackboxing_three\.js/g, /blackboxing_two\.js/g]).then(() => {
|
||||
ok(!getBlackBoxCheckbox(BLACKBOXME_URL).checked,
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg blackbox --invert --glob *blackboxing_t*.js", 3,
|
||||
[/blackboxing_three\.js/g, /blackboxing_two\.js/g]);
|
||||
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXME_URL);
|
||||
ok(bbButton.checked,
|
||||
"blackboxme should be black boxed because it doesn't match the glob.");
|
||||
ok(!getBlackBoxCheckbox(BLACKBOXONE_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXONE_URL);
|
||||
ok(bbButton.checked,
|
||||
"blackbox_one should be black boxed because it doesn't match the glob.");
|
||||
ok(!getBlackBoxCheckbox(TEST_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, TEST_URL);
|
||||
ok(bbButton.checked,
|
||||
"TEST_URL should be black boxed because it doesn't match the glob.");
|
||||
|
||||
ok(getBlackBoxCheckbox(BLACKBOXTWO_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTWO_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_two should not be black boxed because it matches the glob.");
|
||||
ok(getBlackBoxCheckbox(BLACKBOXTHREE_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXTHREE_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_three should not be black boxed because it matches the glob.");
|
||||
});
|
||||
}
|
||||
|
||||
function testUnBlackBoxInvert() {
|
||||
return cmd("dbg unblackbox --invert --glob *blackboxing_t*.js", 3).then(() => {
|
||||
ok(getBlackBoxCheckbox(BLACKBOXME_URL).checked,
|
||||
return Task.spawn(function* () {
|
||||
yield cmd("dbg unblackbox --invert --glob *blackboxing_t*.js", 3);
|
||||
|
||||
let bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXME_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackboxme should be un-black boxed because it does not match the glob.");
|
||||
ok(getBlackBoxCheckbox(BLACKBOXONE_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, BLACKBOXONE_URL);
|
||||
ok(!bbButton.checked,
|
||||
"blackbox_one should be un-black boxed because it does not match the glob.");
|
||||
ok(getBlackBoxCheckbox(TEST_URL).checked,
|
||||
bbButton = yield selectSourceAndGetBlackBoxButton(gPanel, TEST_URL);
|
||||
ok(!bbButton.checked,
|
||||
"TEST_URL should be un-black boxed because it doesn't match the glob.");
|
||||
});
|
||||
}
|
||||
@ -126,9 +152,3 @@ function cmd(aTyped, aEventRepeat = 1, aOutput = "") {
|
||||
helpers.audit(gOptions, [{ setup: aTyped, output: aOutput, exec: {} }])
|
||||
]);
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(url) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\"" + url + "\"] " +
|
||||
".side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
@ -22,8 +22,7 @@ function test() {
|
||||
|
||||
waitForSourceShown(gPanel, "code_ugly.js")
|
||||
.then(testSourceIsUgly)
|
||||
.then(blackBoxSource)
|
||||
.then(waitForThreadEvents.bind(null, gPanel, "blackboxchange"))
|
||||
.then(toggleBlackBoxing.bind(null, gPanel))
|
||||
.then(clickPrettyPrintButton)
|
||||
.then(testSourceIsStillUgly)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
@ -38,12 +37,6 @@ function testSourceIsUgly() {
|
||||
"The source shouldn't be pretty printed yet.");
|
||||
}
|
||||
|
||||
function blackBoxSource() {
|
||||
const checkbox = gDebugger.document.querySelector(
|
||||
".selected .side-menu-widget-item-checkbox");
|
||||
checkbox.click();
|
||||
}
|
||||
|
||||
function clickPrettyPrintButton() {
|
||||
gDebugger.document.getElementById("pretty-print").click();
|
||||
}
|
||||
|
@ -523,3 +523,29 @@ function resumeDebuggerThenCloseAndFinish(aPanel, aFlags = {}) {
|
||||
thread.resume(() => closeDebuggerAndFinish(aPanel, aFlags).then(deferred.resolve));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getBlackBoxButton(aPanel) {
|
||||
return aPanel.panelWin.document.getElementById("black-box");
|
||||
}
|
||||
|
||||
function toggleBlackBoxing(aPanel, aSource = null) {
|
||||
function clickBlackBoxButton() {
|
||||
getBlackBoxButton(aPanel).click();
|
||||
}
|
||||
|
||||
const blackBoxChanged = waitForThreadEvents(aPanel, "blackboxchange");
|
||||
|
||||
if (aSource) {
|
||||
aPanel.panelWin.DebuggerView.Sources.selectedValue = aSource;
|
||||
ensureSourceIs(aPanel, aSource, true).then(clickBlackBoxButton);
|
||||
} else {
|
||||
clickBlackBoxButton();
|
||||
}
|
||||
return blackBoxChanged;
|
||||
}
|
||||
|
||||
function selectSourceAndGetBlackBoxButton(aPanel, aSource) {
|
||||
aPanel.panelWin.DebuggerView.Sources.selectedValue = aSource;
|
||||
return ensureSourceIs(aPanel, aSource, true)
|
||||
.then(getBlackBoxButton.bind(null, aPanel));
|
||||
}
|
||||
|
@ -54,7 +54,14 @@ function submit() {
|
||||
Services.prefs.setIntPref("devtools.debugger.remote-port", port);
|
||||
|
||||
// Initiate the connection
|
||||
let transport = debuggerSocketConnect(host, port);
|
||||
let transport;
|
||||
try {
|
||||
transport = debuggerSocketConnect(host, port);
|
||||
} catch(e) {
|
||||
// Bug 921850: catch rare exception from debuggerSocketConnect
|
||||
showError("unexpected");
|
||||
return;
|
||||
}
|
||||
gClient = new DebuggerClient(transport);
|
||||
let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
|
||||
gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
|
||||
|
@ -18,8 +18,9 @@ To confirm the functionality run mochitests for the following components:
|
||||
|
||||
The sourceeditor component contains imported CodeMirror tests [3]. Some
|
||||
tests were commented out because we don't use that functionality within
|
||||
Firefox (for example Ruby editing mode). Other than that, we don't have
|
||||
any Mozilla-specific patches applied to CodeMirror itself.
|
||||
Firefox (for example Ruby editing mode). The search addon (search.js)
|
||||
was slightly modified to make search UI localizable. Other than that,
|
||||
we don't have any Mozilla-specific patches applied to CodeMirror itself.
|
||||
|
||||
# Addons
|
||||
|
||||
|
@ -45,12 +45,16 @@
|
||||
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
||||
return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
|
||||
}
|
||||
var queryDialog =
|
||||
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
|
||||
var queryDialog;
|
||||
function doSearch(cm, rev) {
|
||||
if (!queryDialog) {
|
||||
queryDialog = cm.l10n('findCmd.promptMessage') +
|
||||
' <input type="text" style="width: 10em"/>';
|
||||
}
|
||||
|
||||
var state = getSearchState(cm);
|
||||
if (state.query) return findNext(cm, rev);
|
||||
dialog(cm, queryDialog, "Search for:", function(query) {
|
||||
dialog(cm, queryDialog, cm.l10n('findCmd.promptMessage'), function(query) {
|
||||
cm.operation(function() {
|
||||
if (!query || state.query) return;
|
||||
state.query = parseQuery(query);
|
||||
|
@ -207,6 +207,10 @@ Editor.prototype = {
|
||||
cm.on("gutterClick", (cm, line) => this.emit("gutterClick", line));
|
||||
cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));
|
||||
|
||||
win.CodeMirror.defineExtension("l10n", (name) => {
|
||||
return L10N.GetStringFromName(name);
|
||||
});
|
||||
|
||||
doc.defaultView.controllers.insertControllerAt(0, controller(this, doc.defaultView));
|
||||
|
||||
this.container = env;
|
||||
|
@ -94,6 +94,9 @@ support-files =
|
||||
test_bug_770099_violation.html
|
||||
test_bug_770099_violation.html^headers^
|
||||
testscript.js
|
||||
test-bug_923281_console_log_filter.html
|
||||
test-bug_923281_test1.js
|
||||
test-bug_923281_test2.js
|
||||
|
||||
[browser_bug664688_sandbox_update_after_navigation.js]
|
||||
[browser_bug_638949_copy_link_location.js]
|
||||
@ -231,3 +234,4 @@ support-files =
|
||||
[browser_webconsole_property_provider.js]
|
||||
[browser_webconsole_scratchpad_panel_link.js]
|
||||
[browser_webconsole_view_source.js]
|
||||
[browser_webconsole_log_file_filter.js]
|
||||
|
@ -0,0 +1,80 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the text filter box works to filter based on filenames
|
||||
// where the logs were generated.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug_923281_console_log_filter.html";
|
||||
|
||||
let hud;
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, consoleOpened);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function consoleOpened(aHud) {
|
||||
hud = aHud;
|
||||
let console = content.console;
|
||||
console.log("sentinel log");
|
||||
waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "sentinel log",
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG
|
||||
}],
|
||||
}).then(testLiveFilteringOnSearchStrings);
|
||||
}
|
||||
|
||||
function testLiveFilteringOnSearchStrings() {
|
||||
is(hud.outputNode.children.length, 4, "number of messages");
|
||||
|
||||
setStringFilter("random");
|
||||
is(countMessageNodes(), 1, "the log nodes not containing string " +
|
||||
"\"random\" are hidden");
|
||||
|
||||
setStringFilter("test2.js");
|
||||
is(countMessageNodes(), 2, "show only log nodes containing string " +
|
||||
"\"test2.js\" or log nodes created from files with filename " +
|
||||
"containing \"test2.js\" as substring.");
|
||||
|
||||
setStringFilter("test1");
|
||||
is(countMessageNodes(), 2, "show only log nodes containing string " +
|
||||
"\"test1\" or log nodes created from files with filename " +
|
||||
"containing \"test1\" as substring.");
|
||||
|
||||
setStringFilter("");
|
||||
is(countMessageNodes(), 4, "show all log nodes on setting filter string " +
|
||||
"as \"\".");
|
||||
|
||||
finishTest();
|
||||
}
|
||||
|
||||
function countMessageNodes() {
|
||||
let outputNode = hud.outputNode;
|
||||
|
||||
let messageNodes = outputNode.querySelectorAll(".message");
|
||||
content.console.log(messageNodes.length);
|
||||
let displayedMessageNodes = 0;
|
||||
let view = hud.iframeWindow;
|
||||
for (let i = 0; i < messageNodes.length; i++) {
|
||||
let computedStyle = view.getComputedStyle(messageNodes[i], null);
|
||||
if (computedStyle.display !== "none") {
|
||||
displayedMessageNodes++;
|
||||
}
|
||||
}
|
||||
|
||||
return displayedMessageNodes;
|
||||
}
|
||||
|
||||
function setStringFilter(aValue)
|
||||
{
|
||||
hud.ui.filterBox.value = aValue;
|
||||
hud.ui.adjustVisibilityOnSearchStringChange();
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Console test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="test-bug_923281_test1.js"></script>
|
||||
<script type="text/javascript" src="test-bug_923281_test2.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
@ -0,0 +1,5 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
console.log("Sample log.");
|
||||
console.log("This log should be filtered when filtered for test2.js.");
|
@ -0,0 +1,4 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
console.log("This is a random text.");
|
@ -873,7 +873,7 @@ WebConsoleFrame.prototype = {
|
||||
let node = nodes[i];
|
||||
|
||||
// hide nodes that match the strings
|
||||
let text = node.clipboardText;
|
||||
let text = node.textContent;
|
||||
|
||||
// if the text matches the words in aSearchString...
|
||||
if (this.stringMatchesFilters(text, searchString)) {
|
||||
|
@ -509,6 +509,8 @@
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
@BINPATH@/components/PhoneNumberService.js
|
||||
@BINPATH@/components/PhoneNumberService.manifest
|
||||
@BINPATH@/components/NotificationStorage.js
|
||||
@BINPATH@/components/NotificationStorage.manifest
|
||||
@BINPATH@/components/AlarmsManager.js
|
||||
@BINPATH@/components/AlarmsManager.manifest
|
||||
@BINPATH@/components/Push.js
|
||||
@ -519,9 +521,6 @@
|
||||
@BINPATH@/components/TCPSocketParentIntermediary.js
|
||||
@BINPATH@/components/TCPSocket.manifest
|
||||
|
||||
@BINPATH@/components/AppProtocolHandler.js
|
||||
@BINPATH@/components/AppProtocolHandler.manifest
|
||||
|
||||
@BINPATH@/components/Payment.js
|
||||
@BINPATH@/components/PaymentFlowInfo.js
|
||||
@BINPATH@/components/PaymentRequestInfo.js
|
||||
|
@ -33,8 +33,12 @@
|
||||
- the button that opens up an options context menu for the debugger UI. -->
|
||||
<!ENTITY debuggerUI.optsButton.tooltip "Debugger Options">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.sources.blackBoxTooltip): This is the tooltip
|
||||
- for the button that black boxes the selected source. -->
|
||||
<!ENTITY debuggerUI.sources.blackBoxTooltip "Toggle Black Boxing">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.sources.prettyPrint): This is the tooltip for the
|
||||
button that pretty prints the selected source. -->
|
||||
- button that pretty prints the selected source. -->
|
||||
<!ENTITY debuggerUI.sources.prettyPrint "Prettify Source">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.pauseExceptions): This is the label for the
|
||||
|
@ -36,15 +36,17 @@ pref("prompts.tab_modal.enabled", true);
|
||||
pref("layers.offmainthreadcomposition.enabled", true);
|
||||
pref("layers.async-pan-zoom.enabled", true);
|
||||
pref("layers.componentalpha.enabled", false);
|
||||
pref("gfx.azpc.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
|
||||
pref("gfx.azpc.pan_repaint_interval", "50"); // prefer 20 fps
|
||||
pref("gfx.azpc.fling_repaint_interval", "50"); // prefer 20 fps
|
||||
pref("gfx.axis.fling_friction", "0.002");
|
||||
pref("gfx.axis.fling_stopped_threshold", "0.2");
|
||||
|
||||
// Prefs to control the async pan/zoom behaviour
|
||||
pref("apz.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
|
||||
pref("apz.pan_repaint_interval", "50"); // prefer 20 fps
|
||||
pref("apz.fling_repaint_interval", "50"); // prefer 20 fps
|
||||
pref("apz.fling_friction", "0.002");
|
||||
pref("apz.fling_stopped_threshold", "0.2");
|
||||
|
||||
// 0 = free, 1 = standard, 2 = sticky
|
||||
pref("apzc.axis_lock_mode", 2);
|
||||
pref("apzc.cross_slide.enabled", true);
|
||||
pref("apz.axis_lock_mode", 2);
|
||||
pref("apz.cross_slide.enabled", true);
|
||||
|
||||
// Enable Microsoft TSF support by default for imes.
|
||||
pref("intl.enable_tsf_support", true);
|
||||
|
BIN
browser/themes/linux/devtools/debugger-blackbox.png
Normal file
BIN
browser/themes/linux/devtools/debugger-blackbox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1005 B |
@ -22,56 +22,29 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
opacity: 0;
|
||||
transition: opacity .15s ease 0s;
|
||||
#black-box {
|
||||
list-style-image: url(debugger-blackbox.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
}
|
||||
|
||||
/* Only show the checkbox when the source is hovered over, is selected, or if it
|
||||
* is not checked. */
|
||||
#sources .side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) {
|
||||
opacity: 1;
|
||||
transition: opacity .15s ease 0s;
|
||||
#black-box[checked] {
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox > .checkbox-spacer-box {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox > .checkbox-spacer-box > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-size: 32px 16px;
|
||||
background-position: -16px 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox[checked] > .checkbox-spacer-box > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
|
||||
#sources .black-boxed {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
|
||||
#sources .black-boxed > .dbg-breakpoint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: none;
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: none;
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
@ -147,7 +120,7 @@
|
||||
/* Sources and breakpoints view */
|
||||
|
||||
.dbg-breakpoint {
|
||||
-moz-margin-start: -14px;
|
||||
-moz-margin-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-breakpoint-line {
|
||||
@ -402,7 +375,8 @@
|
||||
list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png");
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton {
|
||||
#debugger-controls > toolbarbutton,
|
||||
#sources-controls > toolbarbutton {
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
@ -411,11 +385,13 @@
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton:last-of-type {
|
||||
#debugger-controls > toolbarbutton:last-of-type,
|
||||
#sources-controls > toolbarbutton:last-of-type {
|
||||
-moz-border-end-width: 0;
|
||||
}
|
||||
|
||||
#debugger-controls {
|
||||
#debugger-controls,
|
||||
#sources-controls {
|
||||
box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset,
|
||||
0 0 0 1px hsla(210,16%,76%,.15) inset,
|
||||
0 1px 0 hsla(210,16%,76%,.15);
|
||||
|
@ -195,6 +195,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/debugger-step-in.png (devtools/debugger-step-in.png)
|
||||
skin/classic/browser/devtools/debugger-step-out.png (devtools/debugger-step-out.png)
|
||||
skin/classic/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
|
||||
skin/classic/browser/devtools/debugger-blackbox.png (devtools/debugger-blackbox.png)
|
||||
skin/classic/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
|
||||
skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
|
||||
skin/classic/browser/devtools/responsive-horizontal-resizer.png (devtools/responsive-horizontal-resizer.png)
|
||||
|
BIN
browser/themes/osx/devtools/debugger-blackbox.png
Normal file
BIN
browser/themes/osx/devtools/debugger-blackbox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1005 B |
@ -24,52 +24,29 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
opacity: 0;
|
||||
transition: opacity .15s ease-out 0s;
|
||||
#black-box {
|
||||
list-style-image: url(debugger-blackbox.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
}
|
||||
|
||||
/* Only show the checkbox when the source is hovered over, is selected, or if it
|
||||
* is not checked. */
|
||||
#sources .side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) {
|
||||
opacity: 1;
|
||||
transition: opacity .15s ease-out 0s;
|
||||
#black-box[checked] {
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-size: 32px 16px;
|
||||
background-position: -16px 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox[checked] > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
|
||||
#sources .black-boxed {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
|
||||
#sources .black-boxed > .dbg-breakpoint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: none;
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: none;
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
@ -145,7 +122,7 @@
|
||||
/* Sources and breakpoints view */
|
||||
|
||||
.dbg-breakpoint {
|
||||
-moz-margin-start: -14px;
|
||||
-moz-margin-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-breakpoint-line {
|
||||
@ -400,7 +377,8 @@
|
||||
list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png");
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton {
|
||||
#debugger-controls > toolbarbutton,
|
||||
#sources-controls > toolbarbutton {
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
@ -409,11 +387,13 @@
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton:last-of-type {
|
||||
#debugger-controls > toolbarbutton:last-of-type,
|
||||
#sources-controls > toolbarbutton:last-of-type {
|
||||
-moz-border-end-width: 0;
|
||||
}
|
||||
|
||||
#debugger-controls {
|
||||
#debugger-controls,
|
||||
#sources-controls {
|
||||
box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset,
|
||||
0 0 0 1px hsla(210,16%,76%,.15) inset,
|
||||
0 1px 0 hsla(210,16%,76%,.15);
|
||||
|
@ -287,6 +287,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/debugger-step-in.png (devtools/debugger-step-in.png)
|
||||
skin/classic/browser/devtools/debugger-step-out.png (devtools/debugger-step-out.png)
|
||||
skin/classic/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
|
||||
skin/classic/browser/devtools/debugger-blackbox.png (devtools/debugger-blackbox.png)
|
||||
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
|
||||
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
|
||||
skin/classic/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
|
||||
|
BIN
browser/themes/windows/devtools/debugger-blackbox.png
Normal file
BIN
browser/themes/windows/devtools/debugger-blackbox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1005 B |
@ -22,54 +22,29 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
-moz-margin-end: -6px;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
transition: opacity .15s ease 0s;
|
||||
#black-box {
|
||||
list-style-image: url(debugger-blackbox.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
}
|
||||
|
||||
/* Only show the checkbox when the source is hovered over, is selected, or if it
|
||||
* is not checked. */
|
||||
#sources .side-menu-widget-item:hover > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox,
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) {
|
||||
opacity: 1;
|
||||
transition: opacity .15s ease-out 0s;
|
||||
#black-box[checked] {
|
||||
-moz-image-region: rect(0px,32px,16px,16px);
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-size: 32px 16px;
|
||||
background-position: -16px 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox[checked] > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents {
|
||||
#sources .black-boxed {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-contents > .dbg-breakpoint {
|
||||
#sources .black-boxed > .dbg-breakpoint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(ltr) {
|
||||
background-image: none;
|
||||
box-shadow: inset -1px 0 0 #222426;
|
||||
}
|
||||
|
||||
#sources .side-menu-widget-item.selected > .side-menu-widget-item-checkbox:not([checked]) ~ .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
#sources .black-boxed + .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
|
||||
background-image: none;
|
||||
box-shadow: inset 1px 0 0 #222426;
|
||||
}
|
||||
@ -145,7 +120,7 @@
|
||||
/* Sources and breakpoints view */
|
||||
|
||||
.dbg-breakpoint {
|
||||
-moz-margin-start: -14px;
|
||||
-moz-margin-start: 4px;
|
||||
}
|
||||
|
||||
.dbg-breakpoint-line {
|
||||
@ -400,7 +375,8 @@
|
||||
list-style-image: url("chrome://browser/skin/devtools/debugger-step-out.png");
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton {
|
||||
#debugger-controls > toolbarbutton,
|
||||
#sources-controls > toolbarbutton {
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
@ -409,11 +385,13 @@
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
#debugger-controls > toolbarbutton:last-of-type {
|
||||
#debugger-controls > toolbarbutton:last-of-type,
|
||||
#sources-controls > toolbarbutton:last-of-type {
|
||||
-moz-border-end-width: 0;
|
||||
}
|
||||
|
||||
#debugger-controls {
|
||||
#debugger-controls,
|
||||
#sources-controls {
|
||||
box-shadow: 0 1px 0 hsla(209,29%,72%,.15) inset,
|
||||
0 0 0 1px hsla(209,29%,72%,.1) inset,
|
||||
0 0 0 1px hsla(209,29%,72%,.1),
|
||||
|
@ -222,6 +222,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/debugger-step-in.png (devtools/debugger-step-in.png)
|
||||
skin/classic/browser/devtools/debugger-step-out.png (devtools/debugger-step-out.png)
|
||||
skin/classic/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
|
||||
skin/classic/browser/devtools/debugger-blackbox.png (devtools/debugger-blackbox.png)
|
||||
skin/classic/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
|
||||
skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
|
||||
skin/classic/browser/devtools/responsive-horizontal-resizer.png (devtools/responsive-horizontal-resizer.png)
|
||||
@ -497,6 +498,7 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/debugger-step-in.png (devtools/debugger-step-in.png)
|
||||
skin/classic/aero/browser/devtools/debugger-step-out.png (devtools/debugger-step-out.png)
|
||||
skin/classic/aero/browser/devtools/debugger-step-over.png (devtools/debugger-step-over.png)
|
||||
skin/classic/aero/browser/devtools/debugger-blackbox.png (devtools/debugger-blackbox.png)
|
||||
skin/classic/aero/browser/devtools/responsive-se-resizer.png (devtools/responsive-se-resizer.png)
|
||||
skin/classic/aero/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
|
||||
skin/classic/aero/browser/devtools/responsive-horizontal-resizer.png (devtools/responsive-horizontal-resizer.png)
|
||||
|
@ -14,6 +14,7 @@ MOZ_THUMB_INTERWORK=toolchain-default
|
||||
MOZ_FPU=toolchain-default
|
||||
MOZ_FLOAT_ABI=toolchain-default
|
||||
MOZ_SOFT_FLOAT=toolchain-default
|
||||
MOZ_ALIGN=toolchain-default
|
||||
|
||||
MOZ_ARG_WITH_STRING(arch,
|
||||
[ --with-arch=[[type|toolchain-default]]
|
||||
@ -32,6 +33,7 @@ if test -z "$MOZ_ARCH"; then
|
||||
MOZ_ARCH=armv7-a
|
||||
MOZ_FPU=vfp
|
||||
MOZ_FLOAT_ABI=softfp
|
||||
MOZ_ALIGN=no
|
||||
;;
|
||||
arm-Darwin)
|
||||
MOZ_ARCH=toolchain-default
|
||||
@ -159,8 +161,28 @@ no)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$MOZ_ALIGN" in
|
||||
no)
|
||||
align_flag="-mno-unaligned-access"
|
||||
;;
|
||||
yes)
|
||||
align_flag="-munaligned-access"
|
||||
;;
|
||||
*)
|
||||
align_flag=""
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$align_flag"; then
|
||||
_SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $align_flag"
|
||||
AC_MSG_CHECKING(whether alignment flag ($align_flag) is supported)
|
||||
AC_TRY_COMPILE([],[],,align_flag="")
|
||||
CFLAGS="$_SAVE_CFLAGS"
|
||||
fi
|
||||
|
||||
dnl Use echo to avoid accumulating space characters
|
||||
all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag`
|
||||
all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag $align_flag`
|
||||
if test -n "$all_flags"; then
|
||||
_SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$all_flags"
|
||||
|
@ -3986,7 +3986,7 @@ MOZ_PDF_PRINTING=
|
||||
MOZ_DISABLE_CRYPTOLEGACY=
|
||||
NSS_DISABLE_DBM=
|
||||
NECKO_COOKIES=1
|
||||
NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
|
||||
NECKO_PROTOCOLS_DEFAULT="about app data file ftp http res viewsource websocket wyciwyg device"
|
||||
USE_ARM_KUSER=
|
||||
BUILD_CTYPES=1
|
||||
MOZ_USE_NATIVE_POPUP_WINDOWS=
|
||||
@ -8342,6 +8342,11 @@ AC_SUBST(MOZ_POST_DSO_LIB_COMMAND)
|
||||
AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
|
||||
AC_SUBST(MOZ_LINKER_EXTRACT)
|
||||
|
||||
AC_SUBST(MOZ_JSDOWNLOADS)
|
||||
if test -n "$MOZ_JSDOWNLOADS"; then
|
||||
AC_DEFINE(MOZ_JSDOWNLOADS)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Mac bundle name prefix
|
||||
dnl ========================================================
|
||||
|
@ -166,7 +166,8 @@ this.OperatorAppsRegistry = {
|
||||
},
|
||||
appId: undefined,
|
||||
isBrowser: false,
|
||||
isPackage: isPackage
|
||||
isPackage: isPackage,
|
||||
forceSuccessAck: true
|
||||
};
|
||||
|
||||
if (isPackage) {
|
||||
|
@ -2155,6 +2155,20 @@ this.DOMApplicationRegistry = {
|
||||
queuedPackageDownload: {},
|
||||
|
||||
onInstallSuccessAck: function onInstallSuccessAck(aManifestURL) {
|
||||
// If we are offline, register to run when we'll be online.
|
||||
if (Services.io.offline) {
|
||||
let onlineWrapper = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(onlineWrapper,
|
||||
"network:offline-status-changed");
|
||||
DOMApplicationRegistry.onInstallSuccessAck(aManifestURL);
|
||||
}
|
||||
}
|
||||
Services.obs.addObserver(onlineWrapper,
|
||||
"network:offline-status-changed", false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cacheDownload = this.queuedDownload[aManifestURL];
|
||||
if (cacheDownload) {
|
||||
this.startOfflineCacheDownload(cacheDownload.manifest,
|
||||
@ -2371,12 +2385,12 @@ this.DOMApplicationRegistry = {
|
||||
app: appObject,
|
||||
callback: aInstallSuccessCallback
|
||||
};
|
||||
}
|
||||
|
||||
if (aData.app.localInstallPath) {
|
||||
// if it's a local install, there's no content process so just
|
||||
// ack the install
|
||||
this.onInstallSuccessAck(app.manifestURL);
|
||||
}
|
||||
if (aData.forceSuccessAck) {
|
||||
// If it's a local install, there's no content process so just
|
||||
// ack the install.
|
||||
this.onInstallSuccessAck(app.manifestURL);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -762,10 +762,16 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
|
||||
// crash reports directory.
|
||||
else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
|
||||
f = sDirs->crashes;
|
||||
} else {
|
||||
// Not a storage type that we recognize. Return null
|
||||
return;
|
||||
}
|
||||
|
||||
// in testing, we default all device storage types to a temp directory
|
||||
if (f && sDirs->temp) {
|
||||
// In testing, we default all device storage types to a temp directory.
|
||||
// sDirs->temp will only have been initialized (in InitDirs) if the
|
||||
// preference device.storage.testing was set to true. We can't test the
|
||||
// preference directly here, since we may not be on the main thread.
|
||||
if (sDirs->temp) {
|
||||
f = sDirs->temp;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMDesktopNotification.idl',
|
||||
'nsINotificationStorage.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_notification'
|
||||
|
92
dom/interfaces/notification/nsINotificationStorage.idl
Normal file
92
dom/interfaces/notification/nsINotificationStorage.idl
Normal file
@ -0,0 +1,92 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(fb089720-1c5c-11e3-b773-0800200c9a66)]
|
||||
interface nsINotificationStorageCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to pass single notification back
|
||||
* into C++ land for Notification.get return data.
|
||||
*
|
||||
* @param id: a uuid for this notification
|
||||
* @param title: the notification title
|
||||
* @param dir: the notification direction,
|
||||
* possible values are "ltr", "rtl", "auto"
|
||||
* @param lang: the notification language
|
||||
* @param body: the notification body
|
||||
* @param tag: the notification tag
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void handle(in DOMString id,
|
||||
in DOMString title,
|
||||
in DOMString dir,
|
||||
in DOMString lang,
|
||||
in DOMString body,
|
||||
in DOMString tag,
|
||||
in DOMString icon);
|
||||
|
||||
/**
|
||||
* Callback function used to notify C++ the we have returned
|
||||
* all notification objects for this Notification.get call.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for notification persistence layer.
|
||||
*/
|
||||
[scriptable, uuid(b177b080-2a23-11e3-8224-0800200c9a66)]
|
||||
interface nsINotificationStorage : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Add/replace a notification to the persistence layer.
|
||||
*
|
||||
* @param origin: the origin/app of this notification
|
||||
* @param id: a uuid for this notification
|
||||
* @param title: the notification title
|
||||
* @param dir: the notification direction,
|
||||
* possible values are "ltr", "rtl", "auto"
|
||||
* @param lang: the notification language
|
||||
* @param body: the notification body
|
||||
* @param tag: notification tag, will replace any existing
|
||||
* notifications with same origin/tag pair
|
||||
*/
|
||||
void put(in DOMString origin,
|
||||
in DOMString id,
|
||||
in DOMString title,
|
||||
in DOMString dir,
|
||||
in DOMString lang,
|
||||
in DOMString body,
|
||||
in DOMString tag,
|
||||
in DOMString icon);
|
||||
|
||||
/**
|
||||
* Retrieve a list of notifications.
|
||||
*
|
||||
* @param origin: the origin/app for which to fetch notifications from
|
||||
* @param tag: used to fetch only a specific tag
|
||||
* @param callback: nsINotificationStorageCallback, used for
|
||||
* returning notifications objects
|
||||
*/
|
||||
void get(in DOMString origin,
|
||||
in DOMString tag,
|
||||
in nsINotificationStorageCallback aCallback);
|
||||
|
||||
/**
|
||||
* Remove a notification from storage.
|
||||
*
|
||||
* @param origin: the origin/app to delete the notification from
|
||||
* @param id: the uuid for the notification to delete
|
||||
*/
|
||||
void delete(in DOMString origin,
|
||||
in DOMString id);
|
||||
};
|
||||
|
||||
%{C++
|
||||
#define NS_NOTIFICATION_STORAGE_CONTRACTID "@mozilla.org/notificationStorage;1"
|
||||
%}
|
@ -189,6 +189,20 @@ Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
|
||||
prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeResolve(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
{
|
||||
MaybeResolveInternal(aCx, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeReject(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
{
|
||||
MaybeRejectInternal(aCx, aValue);
|
||||
}
|
||||
|
||||
static void
|
||||
EnterCompartment(Maybe<JSAutoCompartment>& aAc, JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
@ -229,9 +243,9 @@ Promise::JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp)
|
||||
PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
|
||||
|
||||
if (task == PromiseCallback::Resolve) {
|
||||
promise->MaybeResolve(aCx, value);
|
||||
promise->MaybeResolveInternal(aCx, value);
|
||||
} else {
|
||||
promise->MaybeReject(aCx, value);
|
||||
promise->MaybeRejectInternal(aCx, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -300,7 +314,7 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
EnterCompartment(ac, cx, value);
|
||||
promise->MaybeReject(cx, value);
|
||||
promise->MaybeRejectInternal(cx, value);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
@ -319,7 +333,7 @@ Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
|
||||
Optional<JS::Handle<JS::Value> > value(aCx, aValue);
|
||||
promise->MaybeResolve(aCx, value);
|
||||
promise->MaybeResolveInternal(aCx, value);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
@ -336,7 +350,7 @@ Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
|
||||
Optional<JS::Handle<JS::Value> > value(aCx, aValue);
|
||||
promise->MaybeReject(aCx, value);
|
||||
promise->MaybeRejectInternal(aCx, value);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
@ -441,9 +455,9 @@ Promise::MaybeReportRejected()
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeResolve(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
Promise::MaybeResolveInternal(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
{
|
||||
if (mResolvePending) {
|
||||
return;
|
||||
@ -453,9 +467,9 @@ Promise::MaybeResolve(JSContext* aCx,
|
||||
}
|
||||
|
||||
void
|
||||
Promise::MaybeReject(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
Promise::MaybeRejectInternal(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aAsynchronous)
|
||||
{
|
||||
if (mResolvePending) {
|
||||
return;
|
||||
|
@ -43,6 +43,11 @@ public:
|
||||
static bool PrefEnabled();
|
||||
static bool EnabledForScope(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
void MaybeResolve(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue);
|
||||
void MaybeReject(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue);
|
||||
|
||||
// WebIDL
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
@ -114,12 +119,12 @@ private:
|
||||
// report it to the error console.
|
||||
void MaybeReportRejected();
|
||||
|
||||
void MaybeResolve(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
void MaybeReject(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
void MaybeResolveInternal(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
void MaybeRejectInternal(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
PromiseTaskSync aSync = AsyncTask);
|
||||
|
||||
void ResolveInternal(JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue,
|
||||
|
@ -6,14 +6,18 @@
|
||||
#include "mozilla/dom/Notification.h"
|
||||
#include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
|
||||
#include "mozilla/dom/OwningNonNull.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "TabChild.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "nsIAlertsService.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsINotificationStorage.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
@ -21,12 +25,124 @@
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsIDOMDesktopNotification.h"
|
||||
#include "nsIAppsService.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
|
||||
|
||||
NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise)
|
||||
: mCount(0),
|
||||
mGlobal(aGlobal.Get()),
|
||||
mWindow(aWindow),
|
||||
mPromise(aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(aPromise);
|
||||
JSContext* cx = aGlobal.GetContext();
|
||||
JSAutoCompartment ac(cx, mGlobal);
|
||||
mNotifications = JS_NewArrayObject(cx, 0, nullptr);
|
||||
HoldData();
|
||||
}
|
||||
|
||||
NS_IMETHOD Handle(const nsAString& aID,
|
||||
const nsAString& aTitle,
|
||||
const nsAString& aDir,
|
||||
const nsAString& aLang,
|
||||
const nsAString& aBody,
|
||||
const nsAString& aTag,
|
||||
const nsAString& aIcon,
|
||||
JSContext* aCx)
|
||||
{
|
||||
MOZ_ASSERT(!aID.IsEmpty());
|
||||
MOZ_ASSERT(!aTitle.IsEmpty());
|
||||
|
||||
NotificationOptions options;
|
||||
options.mDir = Notification::StringToDirection(nsString(aDir));
|
||||
options.mLang = aLang;
|
||||
options.mBody = aBody;
|
||||
options.mTag = aTag;
|
||||
options.mIcon = aIcon;
|
||||
nsRefPtr<Notification> notification = Notification::CreateInternal(mWindow,
|
||||
aID,
|
||||
aTitle,
|
||||
options);
|
||||
JSAutoCompartment ac(aCx, mGlobal);
|
||||
JS::RootedObject scope(aCx, mGlobal);
|
||||
JS::RootedObject element(aCx, notification->WrapObject(aCx, scope));
|
||||
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
|
||||
|
||||
if (!JS_DefineElement(aCx, mNotifications, mCount++,
|
||||
JS::ObjectValue(*element), nullptr, nullptr, 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Done(JSContext* aCx)
|
||||
{
|
||||
JSAutoCompartment ac(aCx, mGlobal);
|
||||
Optional<JS::HandleValue> result(aCx, JS::ObjectValue(*mNotifications));
|
||||
mPromise->MaybeResolve(aCx, result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~NotificationStorageCallback()
|
||||
{
|
||||
DropData();
|
||||
}
|
||||
|
||||
void HoldData()
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
void DropData()
|
||||
{
|
||||
mGlobal = nullptr;
|
||||
mNotifications = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
uint32_t mCount;
|
||||
JS::Heap<JSObject *> mGlobal;
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
JS::Heap<JSObject *> mNotifications;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
||||
tmp->DropData();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
class NotificationPermissionRequest : public nsIContentPermissionRequest,
|
||||
public PCOMContentPermissionRequestChild,
|
||||
public nsIRunnable
|
||||
@ -257,12 +373,15 @@ NotificationTask::Run()
|
||||
{
|
||||
switch (mAction) {
|
||||
case eShow:
|
||||
return mNotification->ShowInternal();
|
||||
mNotification->ShowInternal();
|
||||
break;
|
||||
case eClose:
|
||||
return mNotification->CloseInternal();
|
||||
mNotification->CloseInternal();
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected action for NotificationTask.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(NotificationObserver, nsIObserver)
|
||||
@ -283,50 +402,103 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Notification::Notification(const nsAString& aTitle, const nsAString& aBody,
|
||||
Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
|
||||
NotificationDirection aDir, const nsAString& aLang,
|
||||
const nsAString& aTag, const nsAString& aIconUrl)
|
||||
: mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
|
||||
: mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
|
||||
mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Notification>
|
||||
Notification::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsString tag;
|
||||
if (aOptions.mTag.WasPassed()) {
|
||||
tag.Append(NS_LITERAL_STRING("tag:"));
|
||||
tag.Append(aOptions.mTag.Value());
|
||||
} else {
|
||||
tag.Append(NS_LITERAL_STRING("notag:"));
|
||||
tag.AppendInt(sCount++);
|
||||
}
|
||||
|
||||
nsRefPtr<Notification> notification = new Notification(aTitle,
|
||||
aOptions.mBody,
|
||||
aOptions.mDir,
|
||||
aOptions.mLang,
|
||||
tag,
|
||||
aOptions.mIcon);
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
MOZ_ASSERT(window, "Window should not be null.");
|
||||
notification->BindToOwner(window);
|
||||
nsRefPtr<Notification> notification = CreateInternal(window,
|
||||
EmptyString(),
|
||||
aTitle,
|
||||
aOptions);
|
||||
|
||||
// Queue a task to show the notification.
|
||||
nsCOMPtr<nsIRunnable> showNotificationTask =
|
||||
new NotificationTask(notification, NotificationTask::eShow);
|
||||
NS_DispatchToMainThread(showNotificationTask);
|
||||
NS_DispatchToCurrentThread(showNotificationTask);
|
||||
|
||||
// Persist the notification.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
||||
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
aRv = GetOrigin(window, origin);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString id;
|
||||
notification->GetID(id);
|
||||
aRv = notificationStorage->Put(origin,
|
||||
id,
|
||||
aTitle,
|
||||
DirectionToString(aOptions.mDir),
|
||||
aOptions.mLang,
|
||||
aOptions.mBody,
|
||||
aOptions.mTag,
|
||||
aOptions.mIcon);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return notification.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
already_AddRefed<Notification>
|
||||
Notification::CreateInternal(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aID,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOptions)
|
||||
{
|
||||
nsString id;
|
||||
if (!aID.IsEmpty()) {
|
||||
id = aID;
|
||||
} else {
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
||||
do_GetService("@mozilla.org/uuid-generator;1");
|
||||
NS_ENSURE_TRUE(uuidgen, nullptr);
|
||||
nsID uuid;
|
||||
nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
char buffer[NSID_LENGTH];
|
||||
uuid.ToProvidedString(buffer);
|
||||
NS_ConvertASCIItoUTF16 convertedID(buffer);
|
||||
id = convertedID;
|
||||
}
|
||||
|
||||
nsRefPtr<Notification> notification = new Notification(id,
|
||||
aTitle,
|
||||
aOptions.mBody,
|
||||
aOptions.mDir,
|
||||
aOptions.mLang,
|
||||
aOptions.mTag,
|
||||
aOptions.mIcon);
|
||||
|
||||
notification->BindToOwner(aWindow);
|
||||
return notification.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Notification::ShowInternal()
|
||||
{
|
||||
nsCOMPtr<nsIAlertsService> alertService =
|
||||
@ -337,7 +509,8 @@ Notification::ShowInternal()
|
||||
NotificationPermission::Granted || !alertService) {
|
||||
// We do not have permission to show a notification or alert service
|
||||
// is not available.
|
||||
return DispatchTrustedEvent(NS_LITERAL_STRING("error"));
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("error"));
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
@ -345,17 +518,19 @@ Notification::ShowInternal()
|
||||
if (mIconUrl.Length() > 0) {
|
||||
// Resolve image URL against document base URI.
|
||||
nsIDocument* doc = GetOwner()->GetExtantDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
|
||||
nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
|
||||
NS_ENSURE_TRUE(baseUri, NS_ERROR_UNEXPECTED);
|
||||
nsCOMPtr<nsIURI> srcUri;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
|
||||
mIconUrl, doc, baseUri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (srcUri) {
|
||||
nsAutoCString src;
|
||||
srcUri->GetSpec(src);
|
||||
absoluteUrl = NS_ConvertUTF8toUTF16(src);
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIURI> baseUri = doc->GetBaseURI();
|
||||
if (baseUri) {
|
||||
nsCOMPtr<nsIURI> srcUri;
|
||||
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcUri),
|
||||
mIconUrl, doc, baseUri);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsAutoCString src;
|
||||
srcUri->GetSpec(src);
|
||||
absoluteUrl = NS_ConvertUTF8toUTF16(src);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,7 +538,7 @@ Notification::ShowInternal()
|
||||
|
||||
nsString alertName;
|
||||
rv = GetAlertName(alertName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
nsCOMPtr<nsIAppNotificationService> appNotifier =
|
||||
@ -375,23 +550,26 @@ Notification::ShowInternal()
|
||||
if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
|
||||
nsString manifestUrl = EmptyString();
|
||||
appsService->GetManifestURLByLocalId(appId, manifestUrl);
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
JS::RootedValue val(cx);
|
||||
AppNotificationServiceOptions ops;
|
||||
ops.mTextClickable = true;
|
||||
ops.mManifestURL = manifestUrl;
|
||||
ops.mId = alertName;
|
||||
ops.mDir = DirectionToString(mDir);
|
||||
ops.mLang = mLang;
|
||||
rv = appsService->GetManifestURLByLocalId(appId, manifestUrl);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
JS::RootedValue val(cx);
|
||||
AppNotificationServiceOptions ops;
|
||||
ops.mTextClickable = true;
|
||||
ops.mManifestURL = manifestUrl;
|
||||
ops.mId = alertName;
|
||||
ops.mDir = DirectionToString(mDir);
|
||||
ops.mLang = mLang;
|
||||
|
||||
if (!ops.ToObject(cx, JS::NullPtr(), &val)) {
|
||||
NS_WARNING("Converting dict to object failed!");
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!ops.ToObject(cx, JS::NullPtr(), &val)) {
|
||||
NS_WARNING("Converting dict to object failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
|
||||
observer, val);
|
||||
return;
|
||||
}
|
||||
|
||||
return appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
|
||||
observer, val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -400,9 +578,9 @@ Notification::ShowInternal()
|
||||
// nsIObserver. Thus the cookie must be unique to differentiate observers.
|
||||
nsString uniqueCookie = NS_LITERAL_STRING("notification:");
|
||||
uniqueCookie.AppendInt(sCount++);
|
||||
return alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
|
||||
uniqueCookie, observer, alertName,
|
||||
DirectionToString(mDir), mLang);
|
||||
alertService->ShowAlertNotification(absoluteUrl, mTitle, mBody, true,
|
||||
uniqueCookie, observer, alertName,
|
||||
DirectionToString(mDir), mLang);
|
||||
}
|
||||
|
||||
void
|
||||
@ -490,6 +668,47 @@ Notification::GetPermissionInternal(nsISupports* aGlobal, ErrorResult& aRv)
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Notification::Get(const GlobalObject& aGlobal,
|
||||
const GetNotificationOptions& aFilter,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
MOZ_ASSERT(window);
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (!doc) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString origin;
|
||||
aRv = GetOrigin(window, origin);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
||||
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = new Promise(window);
|
||||
nsCOMPtr<nsINotificationStorageCallback> callback =
|
||||
new NotificationStorageCallback(aGlobal, window, promise);
|
||||
nsString tag = aFilter.mTag.WasPassed() ?
|
||||
aFilter.mTag.Value() :
|
||||
EmptyString();
|
||||
aRv = notificationStorage->Get(origin, tag, callback);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
Notification::PrefEnabled()
|
||||
{
|
||||
@ -511,22 +730,61 @@ Notification::Close()
|
||||
NS_DispatchToMainThread(showNotificationTask);
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
Notification::CloseInternal()
|
||||
{
|
||||
if (!mIsClosed) {
|
||||
nsresult rv;
|
||||
// Don't bail out if notification storage fails, since we still
|
||||
// want to send the close event through the alert service.
|
||||
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
||||
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
|
||||
if (notificationStorage) {
|
||||
nsString origin;
|
||||
rv = GetOrigin(GetOwner(), origin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
notificationStorage->Delete(origin, mID);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAlertsService> alertService =
|
||||
do_GetService(NS_ALERTSERVICE_CONTRACTID);
|
||||
|
||||
if (alertService) {
|
||||
nsString alertName;
|
||||
nsresult rv = GetAlertName(alertName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = alertService->CloseAlert(alertName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = GetAlertName(alertName);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
alertService->CloseAlert(alertName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
nsresult rv;
|
||||
nsIDocument* doc = aWindow->GetExtantDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
|
||||
nsIPrincipal* principal = doc->NodePrincipal();
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
|
||||
|
||||
uint16_t appStatus = principal->GetAppStatus();
|
||||
uint32_t appId = principal->GetAppId();
|
||||
|
||||
if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
|
||||
appId == nsIScriptSecurityManager::NO_APP_ID ||
|
||||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
rv = nsContentUtils::GetUTFOrigin(principal, aOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// If we are in "app code", use manifest URL as unique origin since
|
||||
// multiple apps can share the same origin but not same notifications.
|
||||
nsCOMPtr<nsIAppsService> appsService =
|
||||
do_GetService("@mozilla.org/AppsService;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
appsService->GetManifestURLByLocalId(appId, aOrigin);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -534,20 +792,18 @@ Notification::CloseInternal()
|
||||
nsresult
|
||||
Notification::GetAlertName(nsString& aAlertName)
|
||||
{
|
||||
// Get the notification name that is unique per origin + tag.
|
||||
// The name of the alert is of the form origin#tag
|
||||
|
||||
nsPIDOMWindow* owner = GetOwner();
|
||||
NS_ENSURE_TRUE(owner, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsIDocument* doc = owner->GetExtantDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = nsContentUtils::GetUTFOrigin(doc->NodePrincipal(),
|
||||
aAlertName);
|
||||
// Get the notification name that is unique per origin + tag/ID.
|
||||
// The name of the alert is of the form origin#tag/ID.
|
||||
nsresult rv = GetOrigin(GetOwner(), aAlertName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aAlertName.AppendLiteral("#");
|
||||
aAlertName.Append(mTag);
|
||||
if (!mTag.IsEmpty()) {
|
||||
aAlertName.Append(NS_LITERAL_STRING("tag:"));
|
||||
aAlertName.Append(mTag);
|
||||
} else {
|
||||
aAlertName.Append(NS_LITERAL_STRING("notag:"));
|
||||
aAlertName.Append(mID);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -10,31 +10,37 @@
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
||||
class NotificationObserver;
|
||||
class Promise;
|
||||
|
||||
class Notification : public nsDOMEventTargetHelper
|
||||
{
|
||||
friend class NotificationTask;
|
||||
friend class NotificationPermissionRequest;
|
||||
friend class NotificationObserver;
|
||||
friend class NotificationStorageCallback;
|
||||
|
||||
public:
|
||||
IMPL_EVENT_HANDLER(click)
|
||||
IMPL_EVENT_HANDLER(show)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
IMPL_EVENT_HANDLER(close)
|
||||
|
||||
Notification(const nsAString& aTitle, const nsAString& aBody,
|
||||
NotificationDirection aDir, const nsAString& aLang,
|
||||
const nsAString& aTag, const nsAString& aIconUrl);
|
||||
|
||||
static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOption,
|
||||
ErrorResult& aRv);
|
||||
void GetTitle(nsString& aRetval)
|
||||
void GetID(nsAString& aRetval) {
|
||||
aRetval = mID;
|
||||
}
|
||||
|
||||
void GetTitle(nsAString& aRetval)
|
||||
{
|
||||
aRetval = mTitle;
|
||||
}
|
||||
@ -44,24 +50,22 @@ public:
|
||||
return mDir;
|
||||
}
|
||||
|
||||
void GetLang(nsString& aRetval)
|
||||
void GetLang(nsAString& aRetval)
|
||||
{
|
||||
aRetval = mLang;
|
||||
}
|
||||
|
||||
void GetBody(nsString& aRetval)
|
||||
void GetBody(nsAString& aRetval)
|
||||
{
|
||||
aRetval = mBody;
|
||||
}
|
||||
|
||||
void GetTag(nsString& aRetval)
|
||||
void GetTag(nsAString& aRetval)
|
||||
{
|
||||
if (StringBeginsWith(mTag, NS_LITERAL_STRING("tag:"))) {
|
||||
aRetval = Substring(mTag, 4);
|
||||
}
|
||||
aRetval = mTag;
|
||||
}
|
||||
|
||||
void GetIcon(nsString& aRetval)
|
||||
void GetIcon(nsAString& aRetval)
|
||||
{
|
||||
aRetval = mIconUrl;
|
||||
}
|
||||
@ -73,6 +77,10 @@ public:
|
||||
static NotificationPermission GetPermission(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
|
||||
const GetNotificationOptions& aFilter,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Close();
|
||||
|
||||
static bool PrefEnabled();
|
||||
@ -85,8 +93,17 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
protected:
|
||||
nsresult ShowInternal();
|
||||
nsresult CloseInternal();
|
||||
Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
|
||||
NotificationDirection aDir, const nsAString& aLang,
|
||||
const nsAString& aTag, const nsAString& aIconUrl);
|
||||
|
||||
static already_AddRefed<Notification> CreateInternal(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aID,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOptions);
|
||||
|
||||
void ShowInternal();
|
||||
void CloseInternal();
|
||||
|
||||
static NotificationPermission GetPermissionInternal(nsISupports* aGlobal,
|
||||
ErrorResult& rv);
|
||||
@ -103,8 +120,22 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
static const NotificationDirection StringToDirection(const nsAString& aDirection)
|
||||
{
|
||||
if (aDirection.EqualsLiteral("ltr")) {
|
||||
return NotificationDirection::Ltr;
|
||||
}
|
||||
if (aDirection.EqualsLiteral("rtl")) {
|
||||
return NotificationDirection::Rtl;
|
||||
}
|
||||
return NotificationDirection::Auto;
|
||||
}
|
||||
|
||||
static nsresult GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin);
|
||||
|
||||
nsresult GetAlertName(nsString& aAlertName);
|
||||
|
||||
nsString mID;
|
||||
nsString mTitle;
|
||||
nsString mBody;
|
||||
NotificationDirection mDir;
|
||||
|
270
dom/src/notification/NotificationDB.jsm
Normal file
270
dom/src/notification/NotificationDB.jsm
Normal file
@ -0,0 +1,270 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [];
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- NotificationDB component: " + s + "\n"); }
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageListenerManager");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gEncoder", function() {
|
||||
return new TextEncoder();
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDecoder", function() {
|
||||
return new TextDecoder();
|
||||
});
|
||||
|
||||
|
||||
const NOTIFICATION_STORE_DIR = OS.Constants.Path.profileDir;
|
||||
const NOTIFICATION_STORE_PATH =
|
||||
OS.Path.join(NOTIFICATION_STORE_DIR, "notificationstore.json");
|
||||
|
||||
let NotificationDB = {
|
||||
init: function() {
|
||||
this.notifications = {};
|
||||
this.byTag = {};
|
||||
this.loaded = false;
|
||||
|
||||
this.tasks = []; // read/write operation queue
|
||||
this.runningTask = false;
|
||||
|
||||
ppmm.addMessageListener("Notification:Save", this);
|
||||
ppmm.addMessageListener("Notification:Delete", this);
|
||||
ppmm.addMessageListener("Notification:GetAll", this);
|
||||
},
|
||||
|
||||
// Attempt to read notification file, if it's not there we will create it.
|
||||
load: function(callback) {
|
||||
var promise = OS.File.read(NOTIFICATION_STORE_PATH);
|
||||
promise.then(
|
||||
function onSuccess(data) {
|
||||
try {
|
||||
this.notifications = JSON.parse(gDecoder.decode(data));
|
||||
} catch (e) {
|
||||
if (DEBUG) { debug("Unable to parse file data " + e); }
|
||||
}
|
||||
this.loaded = true;
|
||||
callback && callback();
|
||||
}.bind(this),
|
||||
|
||||
// If read failed, we assume we have no notifications to load.
|
||||
function onFailure(reason) {
|
||||
this.loaded = true;
|
||||
this.createStore(callback);
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
// Creates the notification directory.
|
||||
createStore: function(callback) {
|
||||
var promise = OS.File.makeDir(NOTIFICATION_STORE_DIR, {
|
||||
ignoreExisting: true
|
||||
});
|
||||
promise.then(
|
||||
function onSuccess() {
|
||||
this.createFile(callback);
|
||||
}.bind(this),
|
||||
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("Directory creation failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Creates the notification file once the directory is created.
|
||||
createFile: function(callback) {
|
||||
var promise = OS.File.open(NOTIFICATION_STORE_PATH, {create: true});
|
||||
promise.then(
|
||||
function onSuccess(handle) {
|
||||
callback && callback();
|
||||
},
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("File creation failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Save current notifications to the file.
|
||||
save: function(callback) {
|
||||
var data = gEncoder.encode(JSON.stringify(this.notifications));
|
||||
var promise = OS.File.writeAtomic(NOTIFICATION_STORE_PATH, data);
|
||||
promise.then(
|
||||
function onSuccess() {
|
||||
callback && callback();
|
||||
},
|
||||
function onFailure(reason) {
|
||||
if (DEBUG) { debug("Save failed:" + reason); }
|
||||
callback && callback();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Helper function: callback will be called once file exists and/or is loaded.
|
||||
ensureLoaded: function(callback) {
|
||||
if (!this.loaded) {
|
||||
this.load(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
if (DEBUG) { debug("Received message:" + message.name); }
|
||||
|
||||
switch (message.name) {
|
||||
case "Notification:GetAll":
|
||||
this.queueTask("getall", message.data, function(notifications) {
|
||||
message.target.sendAsyncMessage("Notification:GetAll:Return:OK", {
|
||||
requestID: message.data.requestID,
|
||||
notifications: notifications
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Notification:Save":
|
||||
this.queueTask("save", message.data, function() {
|
||||
message.target.sendAsyncMessage("Notification:Save:Return:OK", {
|
||||
requestID: message.data.requestID
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "Notification:Delete":
|
||||
this.queueTask("delete", message.data, function() {
|
||||
message.target.sendAsyncMessage("Notification:Delete:Return:OK", {
|
||||
requestID: message.data.requestID
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) { debug("Invalid message name" + message.name); }
|
||||
}
|
||||
},
|
||||
|
||||
// We need to make sure any read/write operations are atomic,
|
||||
// so use a queue to run each operation sequentially.
|
||||
queueTask: function(operation, data, callback) {
|
||||
if (DEBUG) { debug("Queueing task: " + operation); }
|
||||
this.tasks.push({
|
||||
operation: operation,
|
||||
data: data,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
// Only run immediately if we aren't currently running another task.
|
||||
if (!this.runningTask) {
|
||||
if (DEBUG) { dump("Task queue was not running, starting now..."); }
|
||||
this.runNextTask();
|
||||
}
|
||||
},
|
||||
|
||||
runNextTask: function() {
|
||||
if (this.tasks.length === 0) {
|
||||
if (DEBUG) { dump("No more tasks to run, queue depleted"); }
|
||||
this.runningTask = false;
|
||||
return;
|
||||
}
|
||||
this.runningTask = true;
|
||||
|
||||
// Always make sure we are loaded before performing any read/write tasks.
|
||||
this.ensureLoaded(function() {
|
||||
var task = this.tasks.shift();
|
||||
|
||||
// Wrap the task callback to make sure we immediately
|
||||
// run the next task after running the original callback.
|
||||
var wrappedCallback = function() {
|
||||
if (DEBUG) { debug("Finishing task: " + task.operation); }
|
||||
task.callback.apply(this, arguments);
|
||||
this.runNextTask();
|
||||
}.bind(this);
|
||||
|
||||
switch (task.operation) {
|
||||
case "getall":
|
||||
this.taskGetAll(task.data, wrappedCallback);
|
||||
break;
|
||||
|
||||
case "save":
|
||||
this.taskSave(task.data, wrappedCallback);
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
this.taskDelete(task.data, wrappedCallback);
|
||||
break;
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
taskGetAll: function(data, callback) {
|
||||
if (DEBUG) { debug("Task, getting all"); }
|
||||
var origin = data.origin;
|
||||
var notifications = [];
|
||||
// Grab only the notifications for specified origin.
|
||||
for (var i in this.notifications[origin]) {
|
||||
notifications.push(this.notifications[origin][i]);
|
||||
}
|
||||
callback(notifications);
|
||||
},
|
||||
|
||||
taskSave: function(data, callback) {
|
||||
if (DEBUG) { debug("Task, saving"); }
|
||||
var origin = data.origin;
|
||||
var notification = data.notification;
|
||||
if (!this.notifications[origin]) {
|
||||
this.notifications[origin] = {};
|
||||
this.byTag[origin] = {};
|
||||
}
|
||||
|
||||
// We might have existing notification with this tag,
|
||||
// if so we need to remove it before saving the new one.
|
||||
if (notification.tag && this.byTag[origin][notification.tag]) {
|
||||
var oldNotification = this.byTag[origin][notification.tag];
|
||||
delete this.notifications[origin][oldNotification.id];
|
||||
this.byTag[origin][notification.tag] = notification;
|
||||
}
|
||||
|
||||
this.notifications[origin][notification.id] = notification;
|
||||
this.save(callback);
|
||||
},
|
||||
|
||||
taskDelete: function(data, callback) {
|
||||
if (DEBUG) { debug("Task, deleting"); }
|
||||
var origin = data.origin;
|
||||
var id = data.id;
|
||||
if (!this.notifications[origin]) {
|
||||
if (DEBUG) { debug("No notifications found for origin: " + origin); }
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we can find the notification to delete.
|
||||
var oldNotification = this.notifications[origin][id];
|
||||
if (!oldNotification) {
|
||||
if (DEBUG) { debug("No notification found with id: " + id); }
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldNotification.tag) {
|
||||
delete this.byTag[origin][oldNotification.tag];
|
||||
}
|
||||
delete this.notifications[origin][id];
|
||||
this.save(callback);
|
||||
}
|
||||
};
|
||||
|
||||
NotificationDB.init();
|
174
dom/src/notification/NotificationStorage.js
Normal file
174
dom/src/notification/NotificationStorage.js
Normal file
@ -0,0 +1,174 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); }
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const NOTIFICATIONSTORAGE_CID = "{37f819b0-0b5c-11e3-8ffd-0800200c9a66}";
|
||||
const NOTIFICATIONSTORAGE_CONTRACTID = "@mozilla.org/notificationStorage;1";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
|
||||
function NotificationStorage() {
|
||||
// cache objects
|
||||
this._notifications = {};
|
||||
this._byTag = {};
|
||||
this._cached = false;
|
||||
|
||||
this._requests = {};
|
||||
this._requestCount = 0;
|
||||
|
||||
// Register for message listeners.
|
||||
cpmm.addMessageListener("Notification:GetAll:Return:OK", this);
|
||||
}
|
||||
|
||||
NotificationStorage.prototype = {
|
||||
|
||||
put: function(origin, id, title, dir, lang, body, tag, icon) {
|
||||
if (DEBUG) { debug("PUT: " + id + ": " + title); }
|
||||
var notification = {
|
||||
id: id,
|
||||
title: title,
|
||||
dir: dir,
|
||||
lang: lang,
|
||||
body: body,
|
||||
tag: tag,
|
||||
icon: icon
|
||||
};
|
||||
|
||||
this._notifications[id] = notification;
|
||||
if (tag) {
|
||||
// We might have existing notification with this tag,
|
||||
// if so we need to remove it from our cache.
|
||||
if (this._byTag[tag]) {
|
||||
var oldNotification = this._byTag[tag];
|
||||
delete this._notifications[oldNotification.id];
|
||||
}
|
||||
|
||||
this._byTag[tag] = notification;
|
||||
};
|
||||
|
||||
cpmm.sendAsyncMessage("Notification:Save", {
|
||||
origin: origin,
|
||||
notification: notification
|
||||
});
|
||||
},
|
||||
|
||||
get: function(origin, tag, callback) {
|
||||
if (DEBUG) { debug("GET: " + tag); }
|
||||
if (this._cached) {
|
||||
this._fetchFromCache(tag, callback);
|
||||
} else {
|
||||
this._fetchFromDB(origin, tag, callback);
|
||||
}
|
||||
},
|
||||
|
||||
delete: function(origin, id) {
|
||||
if (DEBUG) { debug("DELETE: " + id); }
|
||||
var notification = this._notifications[id];
|
||||
if (notification) {
|
||||
if (notification.tag) {
|
||||
delete this._byTag[notification.tag];
|
||||
}
|
||||
delete this._notifications[id];
|
||||
}
|
||||
|
||||
cpmm.sendAsyncMessage("Notification:Delete", {
|
||||
origin: origin,
|
||||
id: id
|
||||
});
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "Notification:GetAll:Return:OK":
|
||||
var request = this._requests[message.data.requestID];
|
||||
delete this._requests[message.data.requestID];
|
||||
this._populateCache(message.data.notifications);
|
||||
this._fetchFromCache(request.tag, request.callback);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG) debug("Unrecognized message: " + message.name);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_fetchFromDB: function(origin, tag, callback) {
|
||||
var request = {
|
||||
origin: origin,
|
||||
tag: tag,
|
||||
callback: callback
|
||||
};
|
||||
var requestID = this._requestCount++;
|
||||
this._requests[requestID] = request;
|
||||
cpmm.sendAsyncMessage("Notification:GetAll", {
|
||||
origin: origin,
|
||||
requestID: requestID
|
||||
});
|
||||
},
|
||||
|
||||
_fetchFromCache: function(tag, callback) {
|
||||
var notifications = [];
|
||||
// If a tag was specified and we have a notification
|
||||
// with this tag, return that. If no tag was specified
|
||||
// simple return all stored notifications.
|
||||
if (tag && this._byTag[tag]) {
|
||||
notifications.push(this._byTag[tag]);
|
||||
} else if (!tag) {
|
||||
for (var id in this._notifications) {
|
||||
notifications.push(this._notifications[id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass each notification back separately.
|
||||
notifications.forEach(function(notification) {
|
||||
try {
|
||||
callback.handle(notification.id,
|
||||
notification.title,
|
||||
notification.dir,
|
||||
notification.lang,
|
||||
notification.body,
|
||||
notification.tag,
|
||||
notification.icon);
|
||||
} catch (e) {
|
||||
if (DEBUG) { debug("Error calling callback handle: " + e); }
|
||||
}
|
||||
});
|
||||
try {
|
||||
callback.done();
|
||||
} catch (e) {
|
||||
if (DEBUG) { debug("Error calling callback done: " + e); }
|
||||
}
|
||||
},
|
||||
|
||||
_populateCache: function(notifications) {
|
||||
notifications.forEach(function(notification) {
|
||||
this._notifications[notification.id] = notification;
|
||||
if (notification.tag) {
|
||||
this._byTag[notification.tag] = notification;
|
||||
}
|
||||
}.bind(this));
|
||||
this._cached = true;
|
||||
},
|
||||
|
||||
classID : Components.ID(NOTIFICATIONSTORAGE_CID),
|
||||
contractID : NOTIFICATIONSTORAGE_CONTRACTID,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINotificationStorage,
|
||||
Ci.nsIMessageListener]),
|
||||
};
|
||||
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NotificationStorage]);
|
3
dom/src/notification/NotificationStorage.manifest
Normal file
3
dom/src/notification/NotificationStorage.manifest
Normal file
@ -0,0 +1,3 @@
|
||||
# NotificationStorage.js
|
||||
component {37f819b0-0b5c-11e3-8ffd-0800200c9a66} NotificationStorage.js
|
||||
contract @mozilla.org/notificationStorage;1 {37f819b0-0b5c-11e3-8ffd-0800200c9a66}
|
@ -6,6 +6,15 @@
|
||||
|
||||
MODULE = 'dom'
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'NotificationStorage.js',
|
||||
'NotificationStorage.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'NotificationDB.jsm'
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'DesktopNotification.h',
|
||||
'Notification.h',
|
||||
|
@ -29,7 +29,3 @@ DIRS += [
|
||||
if CONFIG['MOZ_GAMEPAD']:
|
||||
DIRS += ['gamepad']
|
||||
|
||||
#needs IPC support, also tests do not run successfully in Firefox for now
|
||||
#if CONFIG['MOZ_BUILD_APP'] != 'mobile':
|
||||
# DIRS += ['notification']
|
||||
|
||||
|
81
dom/tests/mochitest/notification/MockServices.js
Normal file
81
dom/tests/mochitest/notification/MockServices.js
Normal file
@ -0,0 +1,81 @@
|
||||
var MockServices = (function () {
|
||||
"use strict";
|
||||
|
||||
const MOCK_ALERTS_CID = SpecialPowers.wrap(SpecialPowers.Components)
|
||||
.ID("{48068bc2-40ab-4904-8afd-4cdfb3a385f3}");
|
||||
const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
|
||||
|
||||
const MOCK_SYSTEM_ALERTS_CID = SpecialPowers.wrap(SpecialPowers.Components)
|
||||
.ID("{e86d888c-e41b-4b78-9104-2f2742a532de}");
|
||||
const SYSTEM_ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/system-alerts-service;1";
|
||||
|
||||
var registrar = SpecialPowers.wrap(SpecialPowers.Components).manager
|
||||
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar);
|
||||
|
||||
var activeNotifications = Object.create(null);
|
||||
|
||||
var mockAlertsService = {
|
||||
showAlertNotification: function(imageUrl, title, text, textClickable,
|
||||
cookie, alertListener, name) {
|
||||
var listener = SpecialPowers.wrap(alertListener);
|
||||
activeNotifications[name] = {
|
||||
listener: listener,
|
||||
cookie: cookie
|
||||
};
|
||||
|
||||
// fake async alert show event
|
||||
setTimeout(function () {
|
||||
listener.observe(null, "alertshow", cookie);
|
||||
}, 100);
|
||||
|
||||
// ?? SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", cookie);
|
||||
},
|
||||
|
||||
showAppNotification: function(imageUrl, title, text, textClickable,
|
||||
manifestURL, alertListener, name) {
|
||||
this.showAlertNotification(imageUrl, title, text, textClickable, "", alertListener, name);
|
||||
},
|
||||
|
||||
closeAlert: function(name) {
|
||||
var notification = activeNotifications[name];
|
||||
if (notification) {
|
||||
notification.listener.observe(null, "alertfinished", notification.cookie);
|
||||
delete activeNotifications[name];
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) ||
|
||||
SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService)) {
|
||||
return this;
|
||||
}
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
createInstance: function(aOuter, aIID) {
|
||||
if (aOuter != null) {
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return this.QueryInterface(aIID);
|
||||
}
|
||||
};
|
||||
mockAlertsService = SpecialPowers.wrapCallbackObject(mockAlertsService);
|
||||
|
||||
// MockServices API
|
||||
return {
|
||||
register: function () {
|
||||
registrar.registerFactory(MOCK_ALERTS_CID, "alerts service",
|
||||
ALERTS_SERVICE_CONTRACT_ID,
|
||||
mockAlertsService);
|
||||
|
||||
registrar.registerFactory(MOCK_SYSTEM_ALERTS_CID, "system alerts service",
|
||||
SYSTEM_ALERTS_SERVICE_CONTRACT_ID,
|
||||
mockAlertsService);
|
||||
},
|
||||
|
||||
unregister: function () {
|
||||
registrar.unregisterFactory(MOCK_ALERTS_CID, mockAlertsService);
|
||||
registrar.unregisterFactory(MOCK_SYSTEM_ALERTS_CID, mockAlertsService);
|
||||
},
|
||||
};
|
||||
})();
|
73
dom/tests/mochitest/notification/NotificationTest.js
Normal file
73
dom/tests/mochitest/notification/NotificationTest.js
Normal file
@ -0,0 +1,73 @@
|
||||
var NotificationTest = (function () {
|
||||
"use strict";
|
||||
|
||||
function info(msg, name) {
|
||||
SimpleTest.info("::Notification Tests::" + (name || ""), msg);
|
||||
}
|
||||
|
||||
function setup_testing_env() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// turn on testing pref (used by notification.cpp, and mock the alerts
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing", true);
|
||||
}
|
||||
|
||||
function teardown_testing_env() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function executeTests(tests, callback) {
|
||||
// context is `this` object in test functions
|
||||
// it can be used to track data between tests
|
||||
var context = {};
|
||||
|
||||
(function executeRemainingTests(remainingTests) {
|
||||
if (!remainingTests.length) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
var nextTest = remainingTests.shift();
|
||||
var finishTest = executeRemainingTests.bind(null, remainingTests);
|
||||
var startTest = nextTest.call.bind(nextTest, context, finishTest);
|
||||
|
||||
try {
|
||||
startTest();
|
||||
// if no callback was defined for test function,
|
||||
// we must manually invoke finish to continue
|
||||
if (nextTest.length === 0) {
|
||||
finishTest();
|
||||
}
|
||||
} catch (e) {
|
||||
ok(false, "Test threw exception!");
|
||||
finishTest();
|
||||
}
|
||||
})(tests);
|
||||
}
|
||||
|
||||
// NotificationTest API
|
||||
return {
|
||||
run: function (tests, callback) {
|
||||
setup_testing_env();
|
||||
|
||||
addLoadEvent(function () {
|
||||
executeTests(tests, function () {
|
||||
teardown_testing_env();
|
||||
callback && callback();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
allowNotifications: function () {
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
|
||||
},
|
||||
|
||||
denyNotifications: function () {
|
||||
SpecialPowers.setBoolPref("notification.prompt.testing.allow", false);
|
||||
},
|
||||
|
||||
clickNotification: function (notification) {
|
||||
// TODO: how??
|
||||
},
|
||||
|
||||
info: info
|
||||
};
|
||||
})();
|
@ -0,0 +1,6 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -1,10 +1,7 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
create_notification.html
|
||||
notification_common.js
|
||||
MockServices.js
|
||||
NotificationTest.js
|
||||
|
||||
[test_basic_notification.html]
|
||||
[test_basic_notification_click.html]
|
||||
[test_leak_windowClose.html]
|
||||
[test_notification_tag.html]
|
||||
[test_web_notifications.html]
|
||||
[test_notification_basics.html]
|
||||
[test_notification_storage.html]
|
||||
|
115
dom/tests/mochitest/notification/test_notification_basics.html
Normal file
115
dom/tests/mochitest/notification/test_notification_basics.html
Normal file
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Notification Basics</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="MockServices.js"></script>
|
||||
<script type="text/javascript" src="NotificationTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script type="text/javascript">
|
||||
|
||||
var info = NotificationTest.info;
|
||||
|
||||
var steps = [
|
||||
function () {
|
||||
info("Test notification spec");
|
||||
ok(Notification, "Notification constructor exists");
|
||||
ok(Notification.permission, "Notification.permission exists");
|
||||
ok(Notification.requestPermission, "Notification.requestPermission exists");
|
||||
ok(Notification.get, "Notification.get exists");
|
||||
},
|
||||
|
||||
function () {
|
||||
info("Test blank requestPermission");
|
||||
Notification.requestPermission();
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Test requestPermission deny");
|
||||
NotificationTest.denyNotifications();
|
||||
Notification.requestPermission(function(perm) {
|
||||
is(perm, "denied", "Permission should be denied.");
|
||||
is(Notification.permission, "denied", "Permission should be denied.");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Test requestPermission grant");
|
||||
NotificationTest.allowNotifications();
|
||||
Notification.requestPermission(function (perm) {
|
||||
is(perm, "granted", "Permission should be granted.");
|
||||
is(Notification.permission, "granted", "Permission should be granted");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
function () {
|
||||
info("Test invalid requestPermission");
|
||||
try {
|
||||
Notification.requestPermission({});
|
||||
ok(false, "Non callable arg to requestPermission should throw");
|
||||
} catch (e) {
|
||||
ok(true, "Non callable arg to requestPermission should throw");
|
||||
}
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Test create notification");
|
||||
|
||||
var options = {
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "This is a notification body",
|
||||
tag: "sometag",
|
||||
icon: "icon.png"
|
||||
};
|
||||
var notification = new Notification("This is a title", options);
|
||||
|
||||
ok(notification, "Notification exists");
|
||||
is(notification.onclick, null, "onclick() should be null");
|
||||
is(notification.onshow, null, "onshow() should be null");
|
||||
is(notification.onerror, null, "onerror() should be null");
|
||||
is(notification.onclose, null, "onclose() should be null");
|
||||
is(typeof notification.close, "function", "close() should exist");
|
||||
|
||||
is(notification.dir, options.dir, "auto should get set");
|
||||
is(notification.lang, options.lang, "lang should get set");
|
||||
is(notification.body, options.body, "body should get set");
|
||||
is(notification.tag, options.tag, "tag should get set");
|
||||
is(notification.icon, options.icon, "icon should get set");
|
||||
|
||||
// store notification in test context
|
||||
this.notification = notification;
|
||||
|
||||
notification.onshow = function () {
|
||||
ok(true, "onshow handler should be called");
|
||||
done();
|
||||
};
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Test closing a notification");
|
||||
var notification = this.notification;
|
||||
|
||||
notification.onclose = function () {
|
||||
ok(true, "onclose handler should be called");
|
||||
done();
|
||||
};
|
||||
|
||||
notification.close();
|
||||
},
|
||||
];
|
||||
|
||||
MockServices.register();
|
||||
NotificationTest.run(steps, function () {
|
||||
MockServices.unregister();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
132
dom/tests/mochitest/notification/test_notification_storage.html
Normal file
132
dom/tests/mochitest/notification/test_notification_storage.html
Normal file
@ -0,0 +1,132 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Notification Basics</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="MockServices.js"></script>
|
||||
<script type="text/javascript" src="NotificationTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script type="text/javascript">
|
||||
|
||||
function deleteAllNotifications() {
|
||||
var promise = Notification.get();
|
||||
promise.then(function (notifications) {
|
||||
notifications.forEach(function(notification) {
|
||||
notification.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var info = NotificationTest.info;
|
||||
|
||||
var steps = [
|
||||
function (done) {
|
||||
info("Test that Notifcation.get fulfills the promise");
|
||||
var promise = Notification.get();
|
||||
ok(promise.then, "should return a promise");
|
||||
|
||||
// Create a new notification to make sure
|
||||
// Notification.get() works while creating
|
||||
var notification = new Notification("this is a test");
|
||||
|
||||
promise.then(function () {
|
||||
ok(true, "promise should be fulfilled");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllNotifications,
|
||||
|
||||
function (done) {
|
||||
info("Test adding a notification, and making sure get returns it");
|
||||
NotificationTest.allowNotifications();
|
||||
var options = {
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "This is a notification body",
|
||||
tag: "sometag",
|
||||
icon: "icon.png"
|
||||
};
|
||||
var notification = new Notification("This is a title", options);
|
||||
var promise = Notification.get();
|
||||
promise.then(function (notifications) {
|
||||
ok(notifications.length, "should return notifications");
|
||||
for (var i = 0; i < notifications.length; i++) {
|
||||
var notification = notifications[i];
|
||||
if (notification.tag === options.tag) {
|
||||
ok(true, "should contain newly created notification");
|
||||
for (var key in options) {
|
||||
is(notification[key], options[key], key + " property should match");
|
||||
}
|
||||
notification.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ok(false, "should contain newly created notification");
|
||||
notification.close();
|
||||
});
|
||||
notification.onclose = done;
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Testing fetching notification by tag filter");
|
||||
var n1 = new Notification("title1", {tag: "tag1"});
|
||||
var n2 = new Notification("title2", {tag: "tag2"});
|
||||
var n3 = new Notification("title3", {tag: "tag3"});
|
||||
var promise = Notification.get({tag: "tag3"});
|
||||
promise.then(function (notifications) {
|
||||
var notification = notifications[0];
|
||||
is(notifications.length, 1, "should return 1 notification");
|
||||
is(notifications[0].title, "title3", "titles should match");
|
||||
is(notifications[0].tag, "tag3", "tags should match");
|
||||
var closeCount = 0;
|
||||
var waitForAll = function () {
|
||||
if (++closeCount >= 3) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
n1.onclose = waitForAll;
|
||||
n2.onclose = waitForAll;
|
||||
n3.onclose = waitForAll;
|
||||
n1.close();
|
||||
n2.close();
|
||||
n3.close();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllNotifications,
|
||||
|
||||
function (done) {
|
||||
info("Testing fetching no notifications");
|
||||
var promise = Notification.get();
|
||||
promise.then(function (notifications) {
|
||||
is(notifications.length, 0, "should return 0 notifications");
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
function (done) {
|
||||
info("Testing fetching multiple notifications");
|
||||
var n1 = new Notification("title1");
|
||||
var n2 = new Notification("title2");
|
||||
var n3 = new Notification("title3");
|
||||
var promise = Notification.get();
|
||||
promise.then(function (notifications) {
|
||||
is(notifications.length, 3, "should return 2 notifications");
|
||||
done();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
MockServices.register();
|
||||
NotificationTest.run(steps, function () {
|
||||
MockServices.unregister();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,100 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=782211
|
||||
-->
|
||||
<head>
|
||||
<title>Bug 782211</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="notification_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782211">Bug 782211</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="text/javascript">
|
||||
if (window.Notification) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function showNotifications() {
|
||||
// Make sure callback is called.
|
||||
Notification.requestPermission(function(perm) {
|
||||
is(perm, "granted", "Permission should be granted.");
|
||||
is(Notification.permission, "granted", "Permission should be granted.");
|
||||
callbackCalled();
|
||||
});
|
||||
|
||||
// Make sure nothing bad happens when requestPermission is called without a callback.
|
||||
Notification.requestPermission();
|
||||
|
||||
try {
|
||||
Notification.requestPermission({});
|
||||
ok(false, "Non callable arugment to request permission should throw exception.");
|
||||
} catch (ex) {
|
||||
ok(true, "Non callable arugment to request permission should throw exception.");
|
||||
}
|
||||
|
||||
var title = "This is a title";
|
||||
|
||||
var notification = new Notification(title);
|
||||
|
||||
is(notification.title, title, "Title should be set");
|
||||
is(notification.dir, "auto", "Dir should default to 'auto'");
|
||||
is(notification.lang, "", "Lang should not be set");
|
||||
is(notification.body, "", "Body should not be set");
|
||||
is(notification.tag, "", "Tag should not be set");
|
||||
|
||||
var options = {
|
||||
dir: "auto",
|
||||
lang: "",
|
||||
body: "This is a notification body",
|
||||
tag: "sometag"
|
||||
};
|
||||
|
||||
var notification = new Notification(title, options);
|
||||
|
||||
is(notification.title, title, "Title should be set");
|
||||
is(notification.dir, options.dir, "Dir should be set");
|
||||
is(notification.lang, options.lang, "Lang should be set");
|
||||
is(notification.body, options.body, "Body should be set");
|
||||
is(notification.tag, options.tag, "Tag should be set");
|
||||
|
||||
notification.onclose = function() {
|
||||
ok(true, "Notification should be closed.");
|
||||
callbackCalled();
|
||||
};
|
||||
|
||||
notification.onshow = function() {
|
||||
ok(true, "Notification should be shown.");
|
||||
notification.close();
|
||||
callbackCalled();
|
||||
};
|
||||
|
||||
notification.onerror = function() {
|
||||
ok(false, "Failed to show notification.");
|
||||
reset_notifications();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
var numCallbacksCalled = 0;
|
||||
|
||||
function callbackCalled() {
|
||||
numCallbacksCalled++;
|
||||
if (numCallbacksCalled == 3) {
|
||||
reset_notifications();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup_notifications(true, true, showNotifications);
|
||||
} else {
|
||||
ok(true, "Notifications are not enabled on the platform.");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
12
dom/webidl/AppInfo.webidl
Normal file
12
dom/webidl/AppInfo.webidl
Normal file
@ -0,0 +1,12 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This dictionnary holds the parameters supporting the app:// protocol.
|
||||
*/
|
||||
dictionary AppInfo
|
||||
{
|
||||
DOMString path = "";
|
||||
boolean isCoreApp = false;
|
||||
};
|
@ -28,6 +28,7 @@ interface DummyInterface : EventTarget {
|
||||
void WifiOptions(optional WifiCommandOptions arg1,
|
||||
optional WifiResultOptions arg2);
|
||||
void AppNotificationServiceOptions(optional AppNotificationServiceOptions arg);
|
||||
void AppInfo(optional AppInfo arg1);
|
||||
};
|
||||
|
||||
interface DummyInterfaceWorkers {
|
||||
|
@ -19,6 +19,9 @@ interface Notification : EventTarget {
|
||||
[Throws]
|
||||
static void requestPermission(optional NotificationPermissionCallback permissionCallback);
|
||||
|
||||
[Throws]
|
||||
static Promise get(optional GetNotificationOptions filter);
|
||||
|
||||
attribute EventHandler onclick;
|
||||
|
||||
attribute EventHandler onshow;
|
||||
@ -52,10 +55,14 @@ dictionary NotificationOptions {
|
||||
NotificationDirection dir = "auto";
|
||||
DOMString lang = "";
|
||||
DOMString body = "";
|
||||
DOMString tag;
|
||||
DOMString tag = "";
|
||||
DOMString icon = "";
|
||||
};
|
||||
|
||||
dictionary GetNotificationOptions {
|
||||
DOMString tag;
|
||||
};
|
||||
|
||||
enum NotificationPermission {
|
||||
"default",
|
||||
"denied",
|
||||
|
@ -19,6 +19,7 @@ WEBIDL_FILES = [
|
||||
'AbstractWorker.webidl',
|
||||
'AnalyserNode.webidl',
|
||||
'AnimationEvent.webidl',
|
||||
'AppInfo.webidl',
|
||||
'AppNotificationServiceOptions.webidl',
|
||||
'ArchiveReader.webidl',
|
||||
'ArchiveRequest.webidl',
|
||||
|
@ -173,14 +173,14 @@ static float gYStationarySizeMultiplier = 2.5f;
|
||||
|
||||
/**
|
||||
* The time period in ms that throttles mozbrowserasyncscroll event.
|
||||
* Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference.
|
||||
* Default is 100ms if there is no "apz.asyncscroll.throttle" in preference.
|
||||
*/
|
||||
|
||||
static int gAsyncScrollThrottleTime = 100;
|
||||
|
||||
/**
|
||||
* The timeout in ms for mAsyncScrollTimeoutTask delay task.
|
||||
* Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference.
|
||||
* Default is 300ms if there is no "apz.asyncscroll.timeout" in preference.
|
||||
*/
|
||||
static int gAsyncScrollTimeout = 300;
|
||||
|
||||
@ -236,21 +236,21 @@ AsyncPanZoomController::InitializeGlobalState()
|
||||
return;
|
||||
sInitialized = true;
|
||||
|
||||
Preferences::AddIntVarCache(&gPanRepaintInterval, "gfx.azpc.pan_repaint_interval", gPanRepaintInterval);
|
||||
Preferences::AddIntVarCache(&gFlingRepaintInterval, "gfx.azpc.fling_repaint_interval", gFlingRepaintInterval);
|
||||
Preferences::AddFloatVarCache(&gMinSkateSpeed, "gfx.azpc.min_skate_speed", gMinSkateSpeed);
|
||||
Preferences::AddIntVarCache(&gTouchListenerTimeout, "gfx.azpc.touch_listener_timeout", gTouchListenerTimeout);
|
||||
Preferences::AddIntVarCache(&gNumPaintDurationSamples, "gfx.azpc.num_paint_duration_samples", gNumPaintDurationSamples);
|
||||
Preferences::AddFloatVarCache(&gTouchStartTolerance, "gfx.azpc.touch_start_tolerance", gTouchStartTolerance);
|
||||
Preferences::AddFloatVarCache(&gXSkateSizeMultiplier, "gfx.azpc.x_skate_size_multiplier", gXSkateSizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gYSkateSizeMultiplier, "gfx.azpc.y_skate_size_multiplier", gYSkateSizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gXStationarySizeMultiplier, "gfx.azpc.x_stationary_size_multiplier", gXStationarySizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gYStationarySizeMultiplier, "gfx.azpc.y_stationary_size_multiplier", gYStationarySizeMultiplier);
|
||||
Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apzc.asyncscroll.throttle", gAsyncScrollThrottleTime);
|
||||
Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout);
|
||||
Preferences::AddBoolVarCache(&gAsyncZoomDisabled, "apzc.asynczoom.disabled", gAsyncZoomDisabled);
|
||||
Preferences::AddBoolVarCache(&gCrossSlideEnabled, "apzc.cross_slide.enabled", gCrossSlideEnabled);
|
||||
Preferences::AddIntVarCache(&gAxisLockMode, "apzc.axis_lock_mode", gAxisLockMode);
|
||||
Preferences::AddIntVarCache(&gPanRepaintInterval, "apz.pan_repaint_interval", gPanRepaintInterval);
|
||||
Preferences::AddIntVarCache(&gFlingRepaintInterval, "apz.fling_repaint_interval", gFlingRepaintInterval);
|
||||
Preferences::AddFloatVarCache(&gMinSkateSpeed, "apz.min_skate_speed", gMinSkateSpeed);
|
||||
Preferences::AddIntVarCache(&gTouchListenerTimeout, "apz.touch_listener_timeout", gTouchListenerTimeout);
|
||||
Preferences::AddIntVarCache(&gNumPaintDurationSamples, "apz.num_paint_duration_samples", gNumPaintDurationSamples);
|
||||
Preferences::AddFloatVarCache(&gTouchStartTolerance, "apz.touch_start_tolerance", gTouchStartTolerance);
|
||||
Preferences::AddFloatVarCache(&gXSkateSizeMultiplier, "apz.x_skate_size_multiplier", gXSkateSizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gYSkateSizeMultiplier, "apz.y_skate_size_multiplier", gYSkateSizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gXStationarySizeMultiplier, "apz.x_stationary_size_multiplier", gXStationarySizeMultiplier);
|
||||
Preferences::AddFloatVarCache(&gYStationarySizeMultiplier, "apz.y_stationary_size_multiplier", gYStationarySizeMultiplier);
|
||||
Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apz.asyncscroll.throttle", gAsyncScrollThrottleTime);
|
||||
Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apz.asyncscroll.timeout", gAsyncScrollTimeout);
|
||||
Preferences::AddBoolVarCache(&gAsyncZoomDisabled, "apz.asynczoom.disabled", gAsyncZoomDisabled);
|
||||
Preferences::AddBoolVarCache(&gCrossSlideEnabled, "apz.cross_slide.enabled", gCrossSlideEnabled);
|
||||
Preferences::AddIntVarCache(&gAxisLockMode, "apz.axis_lock_mode", gAxisLockMode);
|
||||
|
||||
gComputedTimingFunction = new ComputedTimingFunction();
|
||||
gComputedTimingFunction->Init(
|
||||
|
@ -67,12 +67,12 @@ static int gMaxVelocityQueueSize = 5;
|
||||
|
||||
static void ReadAxisPrefs()
|
||||
{
|
||||
Preferences::AddFloatVarCache(&gMaxEventAcceleration, "gfx.axis.max_event_acceleration", gMaxEventAcceleration);
|
||||
Preferences::AddFloatVarCache(&gFlingFriction, "gfx.axis.fling_friction", gFlingFriction);
|
||||
Preferences::AddFloatVarCache(&gVelocityThreshold, "gfx.axis.velocity_threshold", gVelocityThreshold);
|
||||
Preferences::AddFloatVarCache(&gAccelerationMultiplier, "gfx.axis.acceleration_multiplier", gAccelerationMultiplier);
|
||||
Preferences::AddFloatVarCache(&gFlingStoppedThreshold, "gfx.axis.fling_stopped_threshold", gFlingStoppedThreshold);
|
||||
Preferences::AddIntVarCache(&gMaxVelocityQueueSize, "gfx.axis.max_velocity_queue_size", gMaxVelocityQueueSize);
|
||||
Preferences::AddFloatVarCache(&gMaxEventAcceleration, "apz.max_event_acceleration", gMaxEventAcceleration);
|
||||
Preferences::AddFloatVarCache(&gFlingFriction, "apz.fling_friction", gFlingFriction);
|
||||
Preferences::AddFloatVarCache(&gVelocityThreshold, "apz.velocity_threshold", gVelocityThreshold);
|
||||
Preferences::AddFloatVarCache(&gAccelerationMultiplier, "apz.acceleration_multiplier", gAccelerationMultiplier);
|
||||
Preferences::AddFloatVarCache(&gFlingStoppedThreshold, "apz.fling_stopped_threshold", gFlingStoppedThreshold);
|
||||
Preferences::AddIntVarCache(&gMaxVelocityQueueSize, "apz.max_velocity_queue_size", gMaxVelocityQueueSize);
|
||||
}
|
||||
|
||||
class ReadAxisPref MOZ_FINAL : public nsRunnable {
|
||||
|
@ -14,6 +14,7 @@ MOZ_THUMB_INTERWORK=toolchain-default
|
||||
MOZ_FPU=toolchain-default
|
||||
MOZ_FLOAT_ABI=toolchain-default
|
||||
MOZ_SOFT_FLOAT=toolchain-default
|
||||
MOZ_ALIGN=toolchain-default
|
||||
|
||||
MOZ_ARG_WITH_STRING(arch,
|
||||
[ --with-arch=[[type|toolchain-default]]
|
||||
@ -32,6 +33,7 @@ if test -z "$MOZ_ARCH"; then
|
||||
MOZ_ARCH=armv7-a
|
||||
MOZ_FPU=vfp
|
||||
MOZ_FLOAT_ABI=softfp
|
||||
MOZ_ALIGN=no
|
||||
;;
|
||||
arm-Darwin)
|
||||
MOZ_ARCH=toolchain-default
|
||||
@ -159,8 +161,28 @@ no)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$MOZ_ALIGN" in
|
||||
no)
|
||||
align_flag="-mno-unaligned-access"
|
||||
;;
|
||||
yes)
|
||||
align_flag="-munaligned-access"
|
||||
;;
|
||||
*)
|
||||
align_flag=""
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$align_flag"; then
|
||||
_SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $align_flag"
|
||||
AC_MSG_CHECKING(whether alignment flag ($align_flag) is supported)
|
||||
AC_TRY_COMPILE([],[],,align_flag="")
|
||||
CFLAGS="$_SAVE_CFLAGS"
|
||||
fi
|
||||
|
||||
dnl Use echo to avoid accumulating space characters
|
||||
all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag`
|
||||
all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag $align_flag`
|
||||
if test -n "$all_flags"; then
|
||||
_SAVE_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$all_flags"
|
||||
|
@ -13,8 +13,6 @@ public class testBookmark extends AboutHomeTest {
|
||||
private static String BOOKMARK_URL;
|
||||
private static int WAIT_FOR_BOOKMARKED_TIMEOUT = 10000;
|
||||
|
||||
Navigation nav;
|
||||
|
||||
@Override
|
||||
protected int getTestType() {
|
||||
return TEST_MOCHITEST;
|
||||
@ -22,7 +20,6 @@ public class testBookmark extends AboutHomeTest {
|
||||
|
||||
public void testBookmark() {
|
||||
BOOKMARK_URL = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
nav = new Navigation(mDevice);
|
||||
runAboutHomeTest();
|
||||
runMenuTest();
|
||||
}
|
||||
@ -70,7 +67,7 @@ public class testBookmark extends AboutHomeTest {
|
||||
// Bookmark a page for the test
|
||||
loadUrl(BOOKMARK_URL);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
nav.bookmark();
|
||||
toggleBookmark();
|
||||
mAsserter.is(waitForText(StringHelper.BOOKMARK_ADDED_LABEL), true, "bookmark added successfully");
|
||||
}
|
||||
|
||||
@ -78,7 +75,7 @@ public class testBookmark extends AboutHomeTest {
|
||||
// Go back to the page we bookmarked
|
||||
loadUrl(BOOKMARK_URL);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
nav.bookmark();
|
||||
toggleBookmark();
|
||||
mAsserter.is(waitForText(StringHelper.BOOKMARK_REMOVED_LABEL), true, "bookmark removed successfully");
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ function onInstall(aEvent) {
|
||||
return;
|
||||
|
||||
addApplication(aEvent.application);
|
||||
document.getElementById("noapps").className = "hidden";
|
||||
document.getElementById("main-container").classList.remove("hidden");
|
||||
}
|
||||
|
||||
function onUninstall(aEvent) {
|
||||
@ -149,6 +149,6 @@ function onUninstall(aEvent) {
|
||||
let parent = node.parentNode;
|
||||
parent.removeChild(node);
|
||||
if (!parent.firstChild)
|
||||
document.getElementById("noapps").className = "";
|
||||
document.getElementById("main-container").classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
@ -36,17 +36,22 @@
|
||||
|
||||
<div class="header">
|
||||
<div>&aboutApps.header;</div>
|
||||
<div id="header-button" role="button" aria-label="&aboutApps.noApps.middle3;" pref="app.marketplaceURL" onclick="openLink(this);"/>
|
||||
<div id="header-button" role="button" aria-label="&aboutApps.browseMarketplace;" pref="app.marketplaceURL" onclick="openLink(this);"/>
|
||||
</div>
|
||||
<div id="main-container" class="list">
|
||||
<div id="noapps" class="hidden">
|
||||
&aboutApps.noApps.pre;<a id="marketplaceURL" pref="app.marketplaceURL">&aboutApps.noApps.middle3;</a>&aboutApps.noApps.post;
|
||||
</div>
|
||||
|
||||
<div id="main-container" class="list hidden">
|
||||
<div>
|
||||
<div class="spacer" id="spacer1"> </div>
|
||||
<div id="appgrid"/>
|
||||
<div class="spacer" id="spacer1"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-item" role="button" pref="app.marketplaceURL" onclick="openLink(this);">
|
||||
<img class="icon" src="chrome://browser/skin/images/marketplace-logo.png" />
|
||||
<div class="inner">
|
||||
<div id="browse-title" class="title">&aboutApps.browseMarketplace;</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/JNI.jsm");
|
||||
Cu.import('resource://gre/modules/Payment.jsm');
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
Cu.import("resource://gre/modules/ContactService.jsm");
|
||||
Cu.import("resource://gre/modules/NotificationDB.jsm");
|
||||
Cu.import("resource://gre/modules/SpatialNavigation.jsm");
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
|
@ -282,6 +282,8 @@
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
@BINPATH@/components/PhoneNumberService.js
|
||||
@BINPATH@/components/PhoneNumberService.manifest
|
||||
@BINPATH@/components/NotificationStorage.js
|
||||
@BINPATH@/components/NotificationStorage.manifest
|
||||
@BINPATH@/components/SettingsManager.js
|
||||
@BINPATH@/components/SettingsManager.manifest
|
||||
@BINPATH@/components/SettingsService.js
|
||||
@ -385,8 +387,6 @@
|
||||
@BINPATH@/components/Webapps.manifest
|
||||
@BINPATH@/components/AppsService.js
|
||||
@BINPATH@/components/AppsService.manifest
|
||||
@BINPATH@/components/AppProtocolHandler.js
|
||||
@BINPATH@/components/AppProtocolHandler.manifest
|
||||
|
||||
@BINPATH@/components/Push.js
|
||||
@BINPATH@/components/Push.manifest
|
||||
|
@ -5,11 +5,6 @@
|
||||
<!ENTITY aboutApps.title2 "Apps">
|
||||
<!ENTITY aboutApps.header "Your Apps">
|
||||
|
||||
<!-- LOCALIZATION NOTE (aboutApps.noApps.pre): include a trailing space as needed -->
|
||||
<!-- LOCALIZATION NOTE (aboutApps.noApps.middle): avoid leading/trailing spaces, this text is a link -->
|
||||
<!-- LOCALIZATION NOTE (aboutApps.noApps.post): include a starting space as needed -->
|
||||
<!ENTITY aboutApps.noApps.pre "No web apps installed. Get some from the ">
|
||||
<!ENTITY aboutApps.noApps.middle3 "Firefox Marketplace">
|
||||
<!ENTITY aboutApps.noApps.post ".">
|
||||
<!ENTITY aboutApps.browseMarketplace "Browse the Firefox Marketplace">
|
||||
<!ENTITY aboutApps.uninstall "Uninstall">
|
||||
<!ENTITY aboutApps.addToHomescreen "Add to Home Screen">
|
||||
|
@ -2,15 +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/. */
|
||||
|
||||
.inner {
|
||||
background-color: #eef2f5;
|
||||
min-height: 3.8em;
|
||||
padding: 0.5em;
|
||||
|
||||
/* make room for the favicon */
|
||||
-moz-margin-start: 4.5em;
|
||||
}
|
||||
|
||||
.details {
|
||||
width: 100%;
|
||||
}
|
||||
@ -33,14 +24,6 @@
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
#browse-title {
|
||||
margin-top: 1em;
|
||||
background-image: url("chrome://browser/skin/images/chevron.png");
|
||||
background-size: 8px 20px;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#header-button {
|
||||
background-image: url("chrome://browser/skin/images/amo-logo.png"), url("chrome://browser/skin/images/chevron.png");
|
||||
background-size: 20px 20px, 8px 20px;
|
||||
|
@ -7,16 +7,20 @@
|
||||
}
|
||||
|
||||
#header-button {
|
||||
background-image: url("chrome://browser/skin/images/marketplace-logo.png");
|
||||
background-image: url("chrome://browser/skin/images/marketplace-logo.png"), url("chrome://browser/skin/images/chevron.png");
|
||||
background-size: 32px 32px, 8px 20px;
|
||||
background-position: left, right 0.5em center;
|
||||
-moz-padding-start: 2.5em;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
border: 1px solid grey;
|
||||
background-color: white;
|
||||
width: calc(100% - 4em);
|
||||
padding: 2em;
|
||||
background-color: #EEF2F5;
|
||||
border-bottom: 1px solid #BAC2AC;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
@ -44,12 +48,3 @@
|
||||
.app div {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#noapps {
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
@ -65,6 +65,15 @@ body {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.inner {
|
||||
background-color: #eef2f5;
|
||||
min-height: 3.8em;
|
||||
padding: 0.5em;
|
||||
|
||||
/* make room for the favicon */
|
||||
-moz-margin-start: 4.5em;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
body[dir="ltr"] .icon {
|
||||
left: 1.35em;
|
||||
@ -132,3 +141,11 @@ body[dir="ltr"] .icon {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#browse-title {
|
||||
margin-top: 1em;
|
||||
background-image: url("chrome://browser/skin/images/chevron.png");
|
||||
background-size: 8px 20px;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
<Url type="text/html" method="GET" template="http://search.yahoo.com/search">
|
||||
<Param name="p" value="{searchTerms}" />
|
||||
<Param name="ei" value="UTF-8" />
|
||||
<Param name="fr" value="mozilla_mobile_search" />
|
||||
<MozParam name="fr" condition="top2" trueValue="mozilla_mobile_search" falseValue="" />
|
||||
</Url>
|
||||
<SearchForm>http://search.yahoo.com/</SearchForm>
|
||||
</SearchPlugin>
|
||||
|
@ -287,7 +287,7 @@ pref("media.audio_data.enabled", true);
|
||||
// 0 = FREE (No locking at all)
|
||||
// 1 = STANDARD (Once locked, remain locked until scrolling ends)
|
||||
// 2 = STICKY (Allow lock to be broken, with hysteresis)
|
||||
pref("apzc.axis_lock_mode", 0);
|
||||
pref("apz.axis_lock_mode", 0);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
|
||||
|
@ -69,6 +69,7 @@ LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../mime \
|
||||
-I$(srcdir)/../cache \
|
||||
-I$(srcdir)/../protocol/about \
|
||||
-I$(srcdir)/../protocol/app \
|
||||
-I../dns \
|
||||
$(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
|
||||
-I$(srcdir)/../protocol/$(d)) \
|
||||
|
@ -602,6 +602,18 @@
|
||||
{0x93, 0x44, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* netwerk/protocol/app/ classes
|
||||
*/
|
||||
|
||||
#define NS_APPPROTOCOLHANDLER_CID \
|
||||
{ /* {B6ED3030-9999-11d3-A178-0050041CAF44} */ \
|
||||
0xb6ed3030, \
|
||||
0x9999, \
|
||||
0x11d3, \
|
||||
{0xa1, 0x78, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* netwerk/protocol/data/ classes
|
||||
*/
|
||||
|
@ -243,6 +243,7 @@ namespace net {
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
|
||||
}
|
||||
}
|
||||
#include "AppProtocolHandler.h"
|
||||
|
||||
#ifdef NECKO_PROTOCOL_res
|
||||
// resource
|
||||
@ -772,6 +773,7 @@ NS_DEFINE_NAMED_CID(NS_CACHESERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHESERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHENAMESPACE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_APPPROTOCOLHANDLER_CID);
|
||||
#ifdef NECKO_COOKIES
|
||||
NS_DEFINE_NAMED_CID(NS_COOKIEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_COOKIESERVICE_CID);
|
||||
@ -910,6 +912,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
||||
{ &kNS_APPLICATIONCACHESERVICE_CID, false, nullptr, nsApplicationCacheServiceConstructor },
|
||||
{ &kNS_APPLICATIONCACHENAMESPACE_CID, false, nullptr, nsApplicationCacheNamespaceConstructor },
|
||||
{ &kNS_APPLICATIONCACHE_CID, false, nullptr, nsApplicationCacheConstructor },
|
||||
{ &kNS_APPPROTOCOLHANDLER_CID, false, nullptr, AppProtocolHandler::Create },
|
||||
#ifdef NECKO_COOKIES
|
||||
{ &kNS_COOKIEMANAGER_CID, false, nullptr, nsICookieServiceConstructor },
|
||||
{ &kNS_COOKIESERVICE_CID, false, nullptr, nsICookieServiceConstructor },
|
||||
@ -1055,6 +1058,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
|
||||
{ NS_APPLICATIONCACHESERVICE_CONTRACTID, &kNS_APPLICATIONCACHESERVICE_CID },
|
||||
{ NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &kNS_APPLICATIONCACHENAMESPACE_CID },
|
||||
{ NS_APPLICATIONCACHE_CONTRACTID, &kNS_APPLICATIONCACHE_CID },
|
||||
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "app", &kNS_APPPROTOCOLHANDLER_CID },
|
||||
#ifdef NECKO_COOKIES
|
||||
{ NS_COOKIEMANAGER_CONTRACTID, &kNS_COOKIEMANAGER_CID },
|
||||
{ NS_COOKIESERVICE_CONTRACTID, &kNS_COOKIESERVICE_CID },
|
||||
|
424
netwerk/protocol/app/AppProtocolHandler.cpp
Normal file
424
netwerk/protocol/app/AppProtocolHandler.cpp
Normal file
@ -0,0 +1,424 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AppProtocolHandler.h"
|
||||
#include "nsBaseChannel.h"
|
||||
#include "nsJARChannel.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
/**
|
||||
* This dummy channel implementation only provides enough functionality
|
||||
* to return a fake 404 error when the caller asks for an app:// URL
|
||||
* containing an unknown appId.
|
||||
*/
|
||||
class DummyChannel : public nsIJARChannel
|
||||
, nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSICHANNEL
|
||||
NS_DECL_NSIJARCHANNEL
|
||||
|
||||
DummyChannel();
|
||||
|
||||
NS_IMETHODIMP Run();
|
||||
|
||||
private:
|
||||
bool mPending;
|
||||
uint32_t mSuspendCount;
|
||||
nsCOMPtr<nsISupports> mListenerContext;
|
||||
nsCOMPtr<nsIStreamListener> mListener;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsLoadFlags mLoadFlags;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS3(DummyChannel, nsIRequest, nsIChannel, nsIJARChannel)
|
||||
|
||||
DummyChannel::DummyChannel() : mPending(false)
|
||||
, mSuspendCount(0)
|
||||
, mLoadFlags(LOAD_NORMAL)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetName(nsACString &result)
|
||||
{
|
||||
result = "dummy_app_channel";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetStatus(nsresult *aStatus)
|
||||
{
|
||||
*aStatus = NS_ERROR_FILE_NOT_FOUND;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::IsPending(bool *aResult)
|
||||
{
|
||||
*aResult = mPending;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::Suspend()
|
||||
{
|
||||
mSuspendCount++;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::Resume()
|
||||
{
|
||||
if (mSuspendCount <= 0) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (--mSuspendCount == 0) {
|
||||
NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::Open(nsIInputStream**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext)
|
||||
{
|
||||
mListener = aListener;
|
||||
mListenerContext = aContext;
|
||||
mPending = true;
|
||||
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->AddRequest(this, aContext);
|
||||
}
|
||||
|
||||
if (mSuspendCount == 0) {
|
||||
NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIJarChannel, needed for XHR to turn NS_ERROR_FILE_NOT_FOUND into
|
||||
// a 404 error.
|
||||
NS_IMETHODIMP DummyChannel::GetIsUnsafe(bool *aResult)
|
||||
{
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetAppURI(nsIURI *aURI)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::Run()
|
||||
{
|
||||
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mPending = false;
|
||||
rv = mListener->OnStopRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->RemoveRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
rv = SetNotificationCallbacks(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::Cancel(nsresult)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
|
||||
{
|
||||
*aLoadGroup = mLoadGroup;
|
||||
NS_IF_ADDREF(*aLoadGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
|
||||
{
|
||||
mLoadGroup = aLoadGroup;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
*aLoadFlags = mLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
mLoadFlags = aLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetOriginalURI(nsIURI**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetOriginalURI(nsIURI*)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetOwner(nsISupports**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetOwner(nsISupports*)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetNotificationCallbacks(nsIInterfaceRequestor**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetNotificationCallbacks(nsIInterfaceRequestor*)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetSecurityInfo(nsISupports**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentType(nsACString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetContentType(const nsACString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentCharset(nsACString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetContentCharset(const nsACString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentLength(int64_t*)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetContentLength(int64_t)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentDisposition(uint32_t*)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetContentDisposition(uint32_t)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetURI(nsIURI**)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentDispositionFilename(nsAString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::SetContentDispositionFilename(nsAString const &)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DummyChannel::GetContentDispositionHeader(nsACString&)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* app:// protocol implementation.
|
||||
*/
|
||||
|
||||
AppProtocolHandler::AppProtocolHandler() {
|
||||
}
|
||||
|
||||
AppProtocolHandler::~AppProtocolHandler() {
|
||||
mAppInfoCache.Clear();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(AppProtocolHandler, nsIProtocolHandler)
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
AppProtocolHandler::Create(nsISupports* aOuter,
|
||||
const nsIID& aIID,
|
||||
void* *aResult)
|
||||
{
|
||||
AppProtocolHandler* ph = new AppProtocolHandler();
|
||||
if (ph == nullptr) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
NS_ADDREF(ph);
|
||||
nsresult rv = ph->QueryInterface(aIID, aResult);
|
||||
NS_RELEASE(ph);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::GetScheme(nsACString &aResult)
|
||||
{
|
||||
aResult.AssignLiteral("app");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::GetDefaultPort(int32_t *aResult)
|
||||
{
|
||||
// No ports for the app protocol.
|
||||
*aResult = -1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::GetProtocolFlags(uint32_t *aResult)
|
||||
{
|
||||
*aResult = URI_NOAUTH |
|
||||
URI_DANGEROUS_TO_LOAD |
|
||||
URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aCharset, // ignore charset info
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStandardURL> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aCharset, aBaseURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(surl, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
surl->SetMutable(false);
|
||||
NS_ADDREF(*result = url);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We map app://ABCDEF/path/to/file.ext to
|
||||
// jar:file:///path/to/profile/webapps/ABCDEF/application.zip!/path/to/file.ext
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::NewChannel(nsIURI* aUri, nsIChannel* *aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aUri);
|
||||
nsJARChannel* channel = new nsJARChannel();
|
||||
if (!channel) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = aUri->GetHost(host);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString fileSpec;
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
|
||||
rv = url->GetFilePath(fileSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozilla::dom::AppInfo appInfo;
|
||||
|
||||
if (!mAppInfoCache.Get(host, &appInfo)) {
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (!appsService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Value jsInfo;
|
||||
rv = appsService->GetAppInfo(NS_ConvertUTF8toUTF16(host), &jsInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Return a DummyChannel.
|
||||
delete channel;
|
||||
NS_IF_ADDREF(*aResult = new DummyChannel());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
if (!appInfo.Init(cx, JS::Handle<JS::Value>::fromMarkedLocation(&jsInfo)) ||
|
||||
appInfo.mPath.IsEmpty()) {
|
||||
printf_stderr("!! No appInfo for %s\n", host.get());
|
||||
// Return a DummyChannel.
|
||||
delete channel;
|
||||
NS_IF_ADDREF(*aResult = new DummyChannel());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mAppInfoCache.Put(host, appInfo);
|
||||
}
|
||||
|
||||
bool noRemote = (appInfo.mIsCoreApp ||
|
||||
XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
// In-parent and CoreApps can directly access files, so use jar:file://
|
||||
nsAutoCString jarSpec(noRemote ? "jar:file://"
|
||||
: "jar:remoteopenfile://");
|
||||
jarSpec += NS_ConvertUTF16toUTF8(appInfo.mPath) +
|
||||
NS_LITERAL_CSTRING("/application.zip!") +
|
||||
fileSpec;
|
||||
|
||||
nsCOMPtr<nsIURI> jarURI;
|
||||
rv = NS_NewURI(getter_AddRefs(jarURI),
|
||||
jarSpec, nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->Init(jarURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->SetAppURI(aUri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->SetOriginalURI(aUri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ADDREF(*aResult = channel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AppProtocolHandler::AllowPort(int32_t aPort, const char *aScheme, bool *aRetval)
|
||||
{
|
||||
// No port allowed for this scheme.
|
||||
*aRetval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
35
netwerk/protocol/app/AppProtocolHandler.h
Normal file
35
netwerk/protocol/app/AppProtocolHandler.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef AppProtocolHandler_
|
||||
#define AppProtocolHandler_
|
||||
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "mozilla/dom/AppInfoBinding.h"
|
||||
|
||||
class AppProtocolHandler : public nsIProtocolHandler
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIProtocolHandler methods:
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
|
||||
// AppProtocolHandler methods:
|
||||
AppProtocolHandler();
|
||||
virtual ~AppProtocolHandler();
|
||||
|
||||
// Define a Create method to be used with a factory:
|
||||
static nsresult Create(nsISupports* aOuter,
|
||||
const nsIID& aIID,
|
||||
void* *aResult);
|
||||
|
||||
private:
|
||||
nsDataHashtable<nsCStringHashKey, mozilla::dom::AppInfo> mAppInfoCache;
|
||||
};
|
||||
|
||||
#endif /* AppProtocolHandler_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user