Merge m-c to inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2016-01-17 15:06:39 -05:00
commit d8ad3e5215
92 changed files with 1684 additions and 1370 deletions

View File

@ -169,12 +169,10 @@ toolkit/content/contentAreaUtils.js
toolkit/content/widgets/videocontrols.xml
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
toolkit/components/search/nsSearchService.js
toolkit/components/telemetry/healthreport-prefs.js
toolkit/components/url-classifier/**
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/identity/FirefoxAccounts.jsm
toolkit/modules/AppConstants.jsm
toolkit/modules/SessionRecorder.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/extensions/internal/AddonConstants.jsm
toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js

View File

@ -111,6 +111,7 @@ function addWindowTab(window, tabElement) {
if (window)
addListItem(window.tabs, tab);
addListItem(allTabs, tab);
emit(allTabs, "open", tab);
}
// Find tabs in already open windows

View File

@ -1241,6 +1241,21 @@ exports["test ready event after window.open"] = function (assert, done) {
});
}
// related to bug #939496
exports["test tab open event for new window"] = function(assert, done) {
// ensure popups open in a new window and disable popup blocker
setPref(OPEN_IN_NEW_WINDOW_PREF, 2);
setPref(DISABLE_POPUP_PREF, false);
tabs.once('open', function onOpen(window) {
assert.pass("tab open has occured");
window.close(done);
});
// open window to trigger observers
browserWindows.open("about:logo");
};
after(exports, function*(name, assert) {
resetPopupPrefs();
yield cleanUI();

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb",
"git_revision": "ac6083c43e35eae958698e999630fa392f93cc63",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "f9352eba9bf1091faf7b933213968a9eb441ed60",
"revision": "9b062139918bbf3efd1be026eabb64c260d27699",
"repo_path": "integration/gaia-central"
}

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->

View File

@ -21,8 +21,8 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="be3b4cd7c8c4d38c3fa7ebc845bdb408b7bfcdbb"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="b2faf0026571bb3cf9b8b3cb5a252911af951db6"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac6083c43e35eae958698e999630fa392f93cc63"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
<!-- B2G specific things. -->
@ -152,7 +152,7 @@
<default remote="caf" revision="refs/tags/android-5.1.0_r1" sync-j="4"/>
<!-- Nexus 5 specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="fe7df1bc8dd0fd71571505d7be1c31a4ad1e40fb"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="1401762a4eea0b92141e8ff3100f93e9d6556fc8"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="bd18d073ab711f2c9f7f3c352f00b19acd10f5ae"/>
<project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="8b3ffcfdd3d3852eca5488628f8bb2a08acbffa7"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="5d0ae53d9588c3d70c005aec9be94af9a534de16"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="c15b6e266136cd0cdd9b94d0bbed1962d9dd6672"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1452200089000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1452810773000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -2532,6 +2532,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1077" id="helper@vidscrab.com">
<versionRange minVersion="0" maxVersion="*" severity="1">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i15" id="personas@christopher.beard">
<versionRange minVersion="1.6" maxVersion="1.6">

View File

@ -157,7 +157,7 @@ function messageIsCSSError(msg) {
return false;
}
add_task(function checkAllTheCSS() {
add_task(function* checkAllTheCSS() {
let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
@ -236,4 +236,6 @@ add_task(function checkAllTheCSS() {
doc.head.innerHTML = '';
doc = null;
iframe = null;
windowless.close();
windowless = null;
});

View File

@ -10,6 +10,9 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
@ -454,15 +457,10 @@ function gatherTextUnder ( root )
return text;
}
// This function exists for legacy reasons.
function getShellService()
{
var shell = null;
try {
shell = Components.classes["@mozilla.org/browser/shell-service;1"]
.getService(Components.interfaces.nsIShellService);
} catch (e) {
}
return shell;
return ShellService;
}
function isBidiEnabled() {

View File

@ -1342,7 +1342,9 @@ var CustomizableUIInternal = {
}
let tooltip = this.getLocalizedProperty(aWidget, "tooltiptext", additionalTooltipArguments);
node.setAttribute("tooltiptext", tooltip);
if (tooltip) {
node.setAttribute("tooltiptext", tooltip);
}
node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
let commandHandler = this.handleWidgetCommand.bind(this, aWidget, node);
@ -1385,6 +1387,8 @@ var CustomizableUIInternal = {
},
getLocalizedProperty: function(aWidget, aProp, aFormatArgs, aDef) {
const kReqStringProps = ["label"];
if (typeof aWidget == "string") {
aWidget = gPalette.get(aWidget);
}
@ -1395,7 +1399,7 @@ var CustomizableUIInternal = {
// Let widgets pass their own string identifiers or strings, so that
// we can use strings which aren't the default (in case string ids change)
// and so that non-builtin-widgets can also provide labels, tooltips, etc.
if (aWidget[aProp]) {
if (aWidget[aProp] != null) {
name = aWidget[aProp];
// By using this as the default, if a widget provides a full string rather
// than a string ID for localization, we will fall back to that string
@ -1412,7 +1416,9 @@ var CustomizableUIInternal = {
}
return gWidgetsBundle.GetStringFromName(name) || def;
} catch(ex) {
if (!def) {
// If an empty string was explicitly passed, treat it as an actual
// value rather than a missing property.
if (!def && (name != "" || kReqStringProps.includes(aProp))) {
ERROR("Could not localize property '" + name + "'.");
}
}
@ -2336,6 +2342,7 @@ var CustomizableUIInternal = {
this.wrapWidgetEventHandler("onBeforeCreated", widget);
this.wrapWidgetEventHandler("onClick", widget);
this.wrapWidgetEventHandler("onCreated", widget);
this.wrapWidgetEventHandler("onDestroyed", widget);
if (widget.type == "button") {
widget.onCommand = typeof aData.onCommand == "function" ?
@ -2439,6 +2446,9 @@ var CustomizableUIInternal = {
}
}
}
if (widgetNode && widget.onDestroyed) {
widget.onDestroyed(window.document);
}
}
gPalette.delete(aWidgetId);
@ -3172,6 +3182,11 @@ this.CustomizableUI = {
* - onCreated(aNode): Attached to all widgets; a function that will be invoked
* whenever the widget has a DOM node constructed, passing the
* constructed node as an argument.
* - onDestroyed(aDoc): Attached to all non-custom widgets; a function that
* will be invoked after the widget has a DOM node destroyed,
* passing the document from which it was removed. This is
* useful especially for 'view' type widgets that need to
* cleanup after views that were constructed on the fly.
* - onCommand(aEvt): Only useful for button widgets; a function that will be
* invoked when the user activates the button.
* - onClick(aEvt): Attached to all widgets; a function that will be invoked

View File

@ -329,6 +329,7 @@ const PanelUI = {
evt.initCustomEvent("ViewShowing", true, true, viewNode);
viewNode.dispatchEvent(evt);
if (evt.defaultPrevented) {
aAnchor.open = false;
return;
}

View File

@ -326,6 +326,12 @@
]]></body>
</method>
<method name="_shouldSetPosition">
<body><![CDATA[
return this.getAttribute("nosubviews") == "true";
]]></body>
</method>
<method name="_shouldSetHeight">
<body><![CDATA[
return this.getAttribute("nosubviews") != "true";
@ -345,6 +351,19 @@
this.ignoreMutations = false;
]]></body>
</method>
<method name="_adjustContainerHeight">
<body><![CDATA[
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
let height;
if (this.showingSubViewAsMainView) {
height = this._heightOfSubview(this._mainView);
} else {
height = this._mainView.scrollHeight;
}
this._viewContainer.style.height = height + "px";
}
]]></body>
</method>
<method name="_syncContainerWithSubView">
<body><![CDATA[
// Check that this panel is still alive:
@ -361,18 +380,16 @@
<method name="_syncContainerWithMainView">
<body><![CDATA[
// Check that this panel is still alive:
if (!this._panel || !this._panel.parentNode || !this._shouldSetHeight()) {
if (!this._panel || !this._panel.parentNode) {
return;
}
if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
let height;
if (this.showingSubViewAsMainView) {
height = this._heightOfSubview(this._mainView);
} else {
height = this._mainView.scrollHeight;
}
this._viewContainer.style.height = height + "px";
if (this._shouldSetPosition()) {
this._panel.adjustArrowPosition();
}
if (this._shouldSetHeight()) {
this._adjustContainerHeight();
}
]]></body>
</method>

View File

@ -2,13 +2,14 @@
"extends": "../../../toolkit/components/extensions/.eslintrc",
"globals": {
"AllWindowEvents": true,
"currentWindow": true,
"EventEmitter": true,
"IconDetails": true,
"openPanel": true,
"makeWidgetId": true,
"PanelPopup": true,
"TabContext": true,
"AllWindowEvents": true,
"ViewPopup": true,
"WindowEventManager": true,
"WindowListManager": true,
"WindowManager": true,

View File

@ -13,6 +13,8 @@ var {
runSafe,
} = ExtensionUtils;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
@ -24,7 +26,10 @@ function browserActionOf(extension) {
// as the associated popup.
function BrowserAction(options, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id) + "-browser-action";
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-browser-action`;
this.viewId = `PanelUI-webext-${widgetId}-browser-action-view`;
this.widget = null;
this.tabManager = TabManager.for(extension);
@ -37,7 +42,7 @@ function BrowserAction(options, extension) {
this.defaults = {
enabled: true,
title: title,
title: title || extension.name,
badgeText: "",
badgeBackgroundColor: null,
icon: IconDetails.normalize({ path: options.default_icon }, extension,
@ -55,31 +60,60 @@ BrowserAction.prototype = {
build() {
let widget = CustomizableUI.createWidget({
id: this.id,
type: "custom",
viewId: this.viewId,
type: "view",
removable: true,
label: this.defaults.title || this.extension.name,
tooltiptext: this.defaults.title || "",
defaultArea: CustomizableUI.AREA_NAVBAR,
onBuild: document => {
let node = document.createElement("toolbarbutton");
node.id = this.id;
node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button");
onBeforeCreated: document => {
let view = document.createElementNS(XUL_NS, "panelview");
view.id = this.viewId;
view.setAttribute("flex", "1");
document.getElementById("PanelUI-multiView").appendChild(view);
},
onDestroyed: document => {
let view = document.getElementById(this.viewId);
if (view) {
view.remove();
}
},
onCreated: node => {
node.classList.add("badged-button");
node.setAttribute("constrain-size", "true");
this.updateButton(node, this.defaults);
},
onViewShowing: event => {
let document = event.target.ownerDocument;
let tabbrowser = document.defaultView.gBrowser;
node.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
let tab = tabbrowser.selectedTab;
let popup = this.getProperty(tab, "popup");
this.tabManager.addActiveTabPermission(tab);
if (popup) {
this.togglePopup(node, popup);
} else {
this.emit("click");
}
});
let tab = tabbrowser.selectedTab;
let popupURL = this.getProperty(tab, "popup");
this.tabManager.addActiveTabPermission(tab);
return node;
// If the widget has a popup URL defined, we open a popup, but do not
// dispatch a click event to the extension.
// If it has no popup URL defined, we dispatch a click event, but do not
// open a popup.
if (popupURL) {
try {
new ViewPopup(this.extension, event.target, popupURL);
} catch (e) {
Cu.reportError(e);
event.preventDefault();
}
} else {
// This isn't not a hack, but it seems to provide the correct behavior
// with the fewest complications.
event.preventDefault();
this.emit("click");
}
},
});
@ -89,22 +123,12 @@ BrowserAction.prototype = {
this.widget = widget;
},
togglePopup(node, popupResource) {
openPanel(node, popupResource, this.extension);
},
// Update the toolbar button |node| with the tab context data
// in |tabData|.
updateButton(node, tabData) {
if (tabData.title) {
node.setAttribute("tooltiptext", tabData.title);
node.setAttribute("label", tabData.title);
node.setAttribute("aria-label", tabData.title);
} else {
node.removeAttribute("tooltiptext");
node.removeAttribute("label");
node.removeAttribute("aria-label");
}
let title = tabData.title || this.extension.name;
node.setAttribute("tooltiptext", title);
node.setAttribute("label", title);
if (tabData.badgeText) {
node.setAttribute("badge", tabData.badgeText);
@ -162,8 +186,10 @@ BrowserAction.prototype = {
setProperty(tab, prop, value) {
if (tab == null) {
this.defaults[prop] = value;
} else {
} else if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
this.updateOnChange(tab);
@ -226,7 +252,13 @@ extensions.registerSchemaAPI("browserAction", null, (extension, context) => {
setTitle: function(details) {
let tab = details.tabId !== null ? TabManager.getTab(details.tabId) : null;
browserActionOf(extension).setProperty(tab, "title", details.title);
let title = details.title;
// Clear the tab-specific title when given a null string.
if (tab && title == "") {
title = null;
}
browserActionOf(extension).setProperty(tab, "title", title);
},
getTitle: function(details, callback) {

View File

@ -28,7 +28,7 @@ function PageAction(options, extension) {
this.defaults = {
show: false,
title: title,
title: title || extension.name,
icon: IconDetails.normalize({ path: options.default_icon }, extension,
null, true),
popup: popup && extension.baseURI.resolve(popup),
@ -58,7 +58,12 @@ PageAction.prototype = {
// If |tab| is currently selected, updates the page action button to
// reflect the new value.
setProperty(tab, prop, value) {
this.tabContext.get(tab)[prop] = value;
if (value != null) {
this.tabContext.get(tab)[prop] = value;
} else {
delete this.tabContext.get(tab)[prop];
}
if (tab.selected) {
this.updateButton(tab.ownerDocument.defaultView);
}
@ -84,13 +89,9 @@ PageAction.prototype = {
if (tabData.show) {
// Update the title and icon only if the button is visible.
if (tabData.title) {
button.setAttribute("tooltiptext", tabData.title);
button.setAttribute("aria-label", tabData.title);
} else {
button.removeAttribute("tooltiptext");
button.removeAttribute("aria-label");
}
let title = tabData.title || this.extension.name;
button.setAttribute("tooltiptext", title);
button.setAttribute("aria-label", title);
let icon = IconDetails.getURL(tabData.icon, window, this.extension);
button.setAttribute("src", icon);
@ -137,12 +138,16 @@ PageAction.prototype = {
// the any click listeners in the add-on.
handleClick(window) {
let tab = window.gBrowser.selectedTab;
let popup = this.tabContext.get(tab).popup;
let popupURL = this.tabContext.get(tab).popup;
this.tabManager.addActiveTabPermission(tab);
if (popup) {
openPanel(this.getButton(window), popup, this.extension);
// If the widget has a popup URL defined, we open a popup, but do not
// dispatch a click event to the extension.
// If it has no popup URL defined, we dispatch a click event, but do not
// open a popup.
if (popupURL) {
new PanelPopup(this.extension, this.getButton(window), popupURL);
} else {
this.emit("click", tab);
}
@ -213,7 +218,9 @@ extensions.registerSchemaAPI("pageAction", null, (extension, context) => {
setTitle(details) {
let tab = TabManager.getTab(details.tabId);
PageAction.for(extension).setProperty(tab, "title", details.title);
// Clear the tab-specific title when given a null string.
PageAction.for(extension).setProperty(tab, "title", details.title || null);
},
getTitle(details, callback) {

View File

@ -2,12 +2,16 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const INTEGER = /^[1-9]\d*$/;
var {
@ -126,103 +130,203 @@ global.makeWidgetId = id => {
return id.replace(/[^a-z0-9_-]/g, "_");
};
// Open a panel anchored to the given node, containing a browser opened
// to the given URL, owned by the given extension. If |popupURL| is not
// an absolute URL, it is resolved relative to the given extension's
// base URL.
global.openPanel = (node, popupURL, extension) => {
let document = node.ownerDocument;
class BasePopup {
constructor(extension, viewNode, popupURL) {
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
extension.principal, popupURI,
Services.scriptSecurityManager.DISALLOW_SCRIPT);
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
extension.principal, popupURI,
Services.scriptSecurityManager.DISALLOW_SCRIPT);
this.extension = extension;
this.popupURI = popupURI;
this.viewNode = viewNode;
this.window = viewNode.ownerDocument.defaultView;
let panel = document.createElement("panel");
panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
panel.setAttribute("class", "browser-extension-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("role", "group");
this.contentReady = new Promise(resolve => {
this._resolveContentReady = resolve;
});
let anchor;
if (node.localName == "toolbarbutton") {
// Toolbar buttons are a special case. The panel becomes a child of
// the button, and is anchored to the button's icon.
node.appendChild(panel);
anchor = document.getAnonymousElementByAttribute(node, "class", "toolbarbutton-icon");
} else {
// In all other cases, the panel is anchored to the target node
// itself, and is a child of a popupset node.
document.getElementById("mainPopupSet").appendChild(panel);
anchor = node;
this.viewNode.addEventListener(this.DESTROY_EVENT, this);
this.browser = null;
this.browserReady = this.createBrowser(viewNode, popupURI);
}
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
panel.appendChild(browser);
destroy() {
this.browserReady.then(() => {
this.browser.removeEventListener("load", this, true);
this.browser.removeEventListener("DOMTitleChanged", this, true);
this.browser.removeEventListener("DOMWindowClose", this, true);
let titleChangedListener = () => {
panel.setAttribute("aria-label", browser.contentTitle);
};
this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
let context;
let popuphidden = () => {
panel.removeEventListener("popuphidden", popuphidden);
browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
context.unload();
panel.remove();
};
panel.addEventListener("popuphidden", popuphidden);
this.context.unload();
this.browser.remove();
let loadListener = () => {
panel.removeEventListener("load", loadListener);
context = new ExtensionPage(extension, {
type: "popup",
contentWindow: browser.contentWindow,
uri: popupURI,
docShell: browser.docShell,
this.browser = null;
this.viewNode = null;
this.context = null;
});
GlobalManager.injectInDocShell(browser.docShell, extension, context);
browser.setAttribute("src", context.uri.spec);
}
let contentLoadListener = event => {
if (event.target != browser.contentDocument) {
return;
}
browser.removeEventListener("load", contentLoadListener, true);
// Returns the name of the event fired on `viewNode` when the popup is being
// destroyed. This must be implemented by every subclass.
get DESTROY_EVENT() {
throw new Error("Not implemented");
}
let contentViewer = browser.docShell.contentViewer;
let width = {}, height = {};
try {
contentViewer.getContentSize(width, height);
[width, height] = [width.value, height.value];
} catch (e) {
// getContentSize can throw
[width, height] = [400, 400];
}
handleEvent(event) {
switch (event.type) {
case this.DESTROY_EVENT:
this.destroy();
break;
let window = document.defaultView;
width /= window.devicePixelRatio;
height /= window.devicePixelRatio;
width = Math.min(width, 800);
height = Math.min(height, 800);
case "DOMWindowClose":
if (event.target === this.browser.contentWindow) {
event.preventDefault();
this.closePopup();
}
break;
browser.setAttribute("width", width);
browser.setAttribute("height", height);
case "DOMTitleChanged":
this.viewNode.setAttribute("aria-label", this.browser.contentTitle);
break;
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
};
browser.addEventListener("load", contentLoadListener, true);
case "load":
// We use a capturing listener, so we get this event earlier than any
// load listeners in the content page. Resizing after a timeout ensures
// that we calculate the size after the entire event cycle has completed
// (unless someone spins the event loop, anyway), and hopefully after
// the content has made any modifications.
//
// In the future, to match Chrome's behavior, we'll need to update this
// dynamically, probably in response to MozScrolledAreaChanged events.
this.window.setTimeout(() => this.resizeBrowser(), 0);
break;
}
}
browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
};
panel.addEventListener("load", loadListener);
createBrowser(viewNode, popupURI) {
let document = viewNode.ownerDocument;
return panel;
this.browser = document.createElementNS(XUL_NS, "browser");
this.browser.setAttribute("type", "content");
this.browser.setAttribute("disableglobalhistory", "true");
// Note: When using noautohide panels, the popup manager will add width and
// height attributes to the panel, breaking our resize code, if the browser
// starts out smaller than 30px by 10px. This isn't an issue now, but it
// will be if and when we popup debugging.
// This overrides the content's preferred size when displayed in a
// fixed-size, slide-in panel.
this.browser.setAttribute("flex", "1");
viewNode.appendChild(this.browser);
return new Promise(resolve => {
// The first load event is for about:blank.
// We can't finish setting up the browser until the binding has fully
// initialized. Waiting for the first load event guarantees that it has.
let loadListener = event => {
this.browser.removeEventListener("load", loadListener, true);
resolve();
};
this.browser.addEventListener("load", loadListener, true);
}).then(() => {
let { contentWindow } = this.browser;
contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.allowScriptsToClose();
this.context = new ExtensionPage(this.extension, {
type: "popup",
contentWindow,
uri: popupURI,
docShell: this.browser.docShell,
});
GlobalManager.injectInDocShell(this.browser.docShell, this.extension, this.context);
this.browser.setAttribute("src", this.context.uri.spec);
this.browser.addEventListener("load", this, true);
this.browser.addEventListener("DOMTitleChanged", this, true);
this.browser.addEventListener("DOMWindowClose", this, true);
});
}
// Resizes the browser to match the preferred size of the content.
resizeBrowser() {
let width, height;
try {
let w = {}, h = {};
this.browser.docShell.contentViewer.getContentSize(w, h);
width = w.value / this.window.devicePixelRatio;
height = h.value / this.window.devicePixelRatio;
// The width calculation is imperfect, and is often a fraction of a pixel
// too narrow, even after taking the ceiling, which causes lines of text
// to wrap.
width += 1;
} catch (e) {
// getContentSize can throw
[width, height] = [400, 400];
}
width = Math.ceil(Math.min(width, 800));
height = Math.ceil(Math.min(height, 600));
this.browser.style.width = `${width}px`;
this.browser.style.height = `${height}px`;
this._resolveContentReady();
}
}
global.PanelPopup = class PanelPopup extends BasePopup {
constructor(extension, imageNode, popupURL) {
let document = imageNode.ownerDocument;
let panel = document.createElement("panel");
panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
panel.setAttribute("class", "browser-extension-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("role", "group");
document.getElementById("mainPopupSet").appendChild(panel);
super(extension, panel, popupURL);
this.contentReady.then(() => {
panel.openPopup(imageNode, "bottomcenter topright", 0, 0, false, false);
});
}
get DESTROY_EVENT() {
return "popuphidden";
}
destroy() {
super.destroy();
this.viewNode.remove();
}
closePopup() {
this.viewNode.hidePopup();
}
};
global.ViewPopup = class ViewPopup extends BasePopup {
get DESTROY_EVENT() {
return "ViewHiding";
}
closePopup() {
CustomizableUI.hidePanelForNode(this.viewNode);
}
};
// Manages tab-specific context data, and dispatching tab select events

View File

@ -10,6 +10,9 @@
"XPCOMUtils": true,
"Task": true,
// Browser window globals.
"PanelUI": false,
// Test harness globals
"ExtensionTestUtils": false,

View File

@ -2,8 +2,141 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* testTabSwitchContext() {
function* runTests(options) {
function background(getTests) {
// Gets the current details of the browser action, and returns a
// promise that resolves to an object containing them.
function getDetails(tabId) {
return Promise.all([
new Promise(resolve => browser.browserAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getPopup({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeText({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeBackgroundColor({tabId}, resolve))]
).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1],
badge: details[2],
badgeBackgroundColor: details[3] });
});
}
function checkDetails(expecting, tabId) {
return getDetails(tabId).then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
browser.test.assertEq(expecting.badge, details.badge,
"expected value from getBadge");
browser.test.assertEq(String(expecting.badgeBackgroundColor),
String(details.badgeBackgroundColor),
"expected value from getBadgeBackgroundColor");
});
}
let expectDefaults = expecting => {
return checkDetails(expecting);
};
let tabs = [];
let tests = getTests(tabs, expectDefaults);
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
// Check that the API returns the expected values, and then
// run the next test.
new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
return checkDetails(expecting, tabs[0].id);
}).then(() => {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
});
});
}
browser.test.onMessage.addListener((msg) => {
if (msg != "runNextTest") {
browser.test.fail("Expecting 'runNextTest' message");
}
nextTest();
});
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
background: `(${background})(${options.getTests})`,
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
function checkDetails(details) {
let button = document.getElementById(browserActionId);
ok(button, "button exists");
let title = details.title || options.manifest.name;
is(button.getAttribute("image"), details.icon, "icon URL is correct");
is(button.getAttribute("tooltiptext"), title, "image title is correct");
is(button.getAttribute("label"), title, "image label is correct");
is(button.getAttribute("badge"), details.badge, "badge text is correct");
is(button.getAttribute("disabled") == "true", Boolean(details.disabled), "disabled state is correct");
if (details.badge && details.badgeBackgroundColor) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, "class", "toolbarbutton-badge");
let badgeColor = window.getComputedStyle(badge).backgroundColor;
let color = details.badgeBackgroundColor;
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
is(badgeColor, expectedColor, "badge color is correct");
}
// TODO: Popup URL.
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
}
add_task(function* testTabSwitchContext() {
yield runTests({
manifest: {
"browser_action": {
"default_icon": "default.png",
@ -13,7 +146,7 @@ add_task(function* testTabSwitchContext() {
"permissions": ["tabs"],
},
background: function() {
getTests(tabs, expectDefaults) {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
@ -50,10 +183,7 @@ add_task(function* testTabSwitchContext() {
"badgeBackgroundColor": [0, 0xff, 0, 0xff] },
];
let tabs = [];
let expectDefaults;
let tests = [
return [
expect => {
browser.test.log("Initial state, expect default properties.");
expectDefaults(details[0]).then(() => {
@ -157,124 +287,82 @@ add_task(function* testTabSwitchContext() {
});
},
];
// Gets the current details of the browser action, and returns a
// promise that resolves to an object containing them.
function getDetails(tabId) {
return Promise.all([
new Promise(resolve => browser.browserAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getPopup({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeText({tabId}, resolve)),
new Promise(resolve => browser.browserAction.getBadgeBackgroundColor({tabId}, resolve))]
).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1],
badge: details[2],
badgeBackgroundColor: details[3] });
});
}
function checkDetails(expecting, tabId) {
return getDetails(tabId).then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
browser.test.assertEq(expecting.badge, details.badge,
"expected value from getBadge");
browser.test.assertEq(String(expecting.badgeBackgroundColor),
String(details.badgeBackgroundColor),
"expected value from getBadgeBackgroundColor");
});
}
expectDefaults = expecting => {
return checkDetails(expecting);
};
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
// Check that the API returns the expected values, and then
// run the next test.
new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
return checkDetails(expecting, tabs[0].id);
}).then(() => {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
});
});
}
browser.test.onMessage.addListener((msg) => {
if (msg != "runNextTest") {
browser.test.fail("Expecting 'runNextTest' message");
}
nextTest();
});
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
function checkDetails(details) {
let button = document.getElementById(browserActionId);
ok(button, "button exists");
is(button.getAttribute("image"), details.icon, "icon URL is correct");
is(button.getAttribute("tooltiptext"), details.title, "image title is correct");
is(button.getAttribute("label"), details.title, "image label is correct");
is(button.getAttribute("aria-label"), details.title, "image aria-label is correct");
is(button.getAttribute("badge"), details.badge, "badge text is correct");
is(button.getAttribute("disabled") == "true", Boolean(details.disabled), "disabled state is correct");
if (details.badge && details.badgeBackgroundColor) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, "class", "toolbarbutton-badge");
let badgeColor = window.getComputedStyle(badge).backgroundColor;
let color = details.badgeBackgroundColor;
let expectedColor = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
is(badgeColor, expectedColor, "badge color is correct");
}
// TODO: Popup URL.
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
});
add_task(function* testDefaultTitle() {
yield runTests({
manifest: {
"name": "Foo Extension",
"browser_action": {
"default_icon": "icon.png",
},
"permissions": ["tabs"],
},
getTests(tabs, expectDefaults) {
let details = [
{ "title": "Foo Extension",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Foo Title",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Bar Title",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
{ "title": "",
"popup": "",
"badge": "",
"badgeBackgroundColor": null,
"icon": browser.runtime.getURL("icon.png") },
];
return [
expect => {
browser.test.log("Initial state. Expect extension title as default title.");
expectDefaults(details[0]).then(() => {
expect(details[0]);
});
},
expect => {
browser.test.log("Change the title. Expect new title.");
browser.browserAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
expectDefaults(details[0]).then(() => {
expect(details[1]);
});
},
expect => {
browser.test.log("Change the default. Expect same properties.");
browser.browserAction.setTitle({ title: "Bar Title" });
expectDefaults(details[2]).then(() => {
expect(details[1]);
});
},
expect => {
browser.test.log("Clear the title. Expect new default title.");
browser.browserAction.setTitle({ tabId: tabs[0], title: "" });
expectDefaults(details[2]).then(() => {
expect(details[2]);
});
},
expect => {
browser.test.log("Set default title to null string. Expect null string from API, extension title in UI.");
browser.browserAction.setTitle({ title: "" });
expectDefaults(details[3]).then(() => {
expect(details[3]);
});
},
];
},
});
});

View File

@ -2,21 +2,7 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.popupOpen) {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
add_task(function* testPageActionPopup() {
function* testInArea(area) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
@ -116,30 +102,34 @@ add_task(function* testPageActionPopup() {
},
});
let panelId = makeWidgetId(extension.id) + "-panel";
extension.onMessage("send-click", () => {
clickBrowserAction(extension);
});
let widget;
extension.onMessage("next-test", Task.async(function* () {
let panel = document.getElementById(panelId);
if (panel) {
yield promisePopupShown(panel);
panel.hidePopup();
panel = document.getElementById(panelId);
is(panel, null, "panel successfully removed from document after hiding");
if (!widget) {
widget = getBrowserActionWidget(extension);
CustomizableUI.addWidgetToArea(widget.id, area);
}
yield closeBrowserAction(extension);
extension.sendMessage("next-test");
}));
yield Promise.all([extension.startup(), extension.awaitFinish("browseraction-tests-done")]);
yield extension.unload();
let panel = document.getElementById(panelId);
is(panel, null, "browserAction panel removed from document");
let view = document.getElementById(widget.viewId);
is(view, null, "browserAction view removed from document");
}
add_task(function* testBrowserActionInToolbar() {
yield testInArea(CustomizableUI.AREA_NAVBAR);
});
add_task(function* testBrowserActionInPanel() {
yield testInArea(CustomizableUI.AREA_PANEL);
});

View File

@ -33,23 +33,13 @@ add_task(function* () {
yield extension.startup();
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
// Do this a few times to make sure the pop-up is reloaded each time.
for (let i = 0; i < 3; i++) {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
clickBrowserAction(extension);
yield extension.awaitMessage("popup");
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension);
}
yield extension.unload();

View File

@ -110,23 +110,13 @@ add_task(function* () {
yield checkWindow("background", winId2, "win2");
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
yield clickBrowserAction(extension, win);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension, win);
}
// Set focus to some other window.

View File

@ -116,25 +116,21 @@ add_task(function* () {
yield checkViews("background", 2, 0);
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true,
});
node.dispatchEvent(evt);
yield clickBrowserAction(extension, win);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
closeBrowserAction(extension, win);
}
// The popup occasionally closes prematurely if we open it immediately here.
// I'm not sure what causes it to close (it's something internal, and seems to
// be focus-related, but it's not caused by JS calling hidePopup), but even a
// short timeout seems to consistently fix it.
yield new Promise(resolve => win1.setTimeout(resolve, 10));
yield triggerPopup(win1, function*() {
yield checkViews("background", 2, 1);
yield checkViews("popup", 2, 1);

View File

@ -2,18 +2,165 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* testTabSwitchContext() {
function* runTests(options) {
function background(getTests) {
let tabs;
let tests;
// Gets the current details of the page action, and returns a
// promise that resolves to an object containing them.
function getDetails() {
return new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
let tabId = tabs[0].id;
return Promise.all([
new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
}).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1] });
});
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
function finish() {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
}
if (expecting) {
// Check that the API returns the expected values, and then
// run the next test.
getDetails().then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
finish();
});
} else {
finish();
}
});
}
function runTests() {
tabs = [];
tests = getTests(tabs);
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
background: `(${background})(${options.getTests})`,
});
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let currentWindow = window;
let windows = [];
function checkDetails(details) {
let image = currentWindow.document.getElementById(pageActionId);
if (details == null) {
ok(image == null || image.hidden, "image is hidden");
} else {
ok(image, "image exists");
is(image.src, details.icon, "icon URL is correct");
let title = details.title || options.manifest.name;
is(image.getAttribute("tooltiptext"), title, "image title is correct");
is(image.getAttribute("aria-label"), title, "image aria-label is correct");
// TODO: Popup URL.
}
}
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
BrowserTestUtils.openNewBrowserWindow().then(window => {
windows.push(window);
currentWindow = window;
return focusWindow(window);
}).then(() => {
extension.sendMessage("runTests");
});
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
currentWindow = null;
for (let win of windows.splice(0)) {
node = win.document.getElementById(pageActionId);
is(node, null, "pageAction image removed from second document");
yield BrowserTestUtils.closeWindow(win);
}
}
add_task(function* testTabSwitchContext() {
yield runTests({
manifest: {
"name": "Foo Extension",
"page_action": {
"default_icon": "default.png",
"default_popup": "default.html",
"default_title": "Default Title \u263a",
},
"permissions": ["tabs"],
},
background: function() {
getTests(tabs) {
let details = [
{ "icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
@ -24,11 +171,12 @@ add_task(function* testTabSwitchContext() {
{ "icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Title 2" },
{ "icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Default Title \u263a" },
];
let tabs;
let tests;
let allTests = [
return [
expect => {
browser.test.log("Initial state. No icon visible.");
expect(null);
@ -60,6 +208,12 @@ add_task(function* testTabSwitchContext() {
expect(details[2]);
},
expect => {
browser.test.log("Clear the title. Expect default title.");
browser.pageAction.setTitle({ tabId: tabs[1], title: "" });
expect(details[3]);
},
expect => {
browser.test.log("Navigate to a new page. Expect icon hidden.");
@ -104,135 +258,53 @@ add_task(function* testTabSwitchContext() {
expect(null);
},
];
// Gets the current details of the page action, and returns a
// promise that resolves to an object containing them.
function getDetails() {
return new Promise(resolve => {
return browser.tabs.query({ active: true, currentWindow: true }, resolve);
}).then(tabs => {
let tabId = tabs[0].id;
return Promise.all([
new Promise(resolve => browser.pageAction.getTitle({tabId}, resolve)),
new Promise(resolve => browser.pageAction.getPopup({tabId}, resolve))]);
}).then(details => {
return Promise.resolve({ title: details[0],
popup: details[1] });
});
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(expecting => {
function finish() {
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
}
if (expecting) {
// Check that the API returns the expected values, and then
// run the next test.
getDetails().then(details => {
browser.test.assertEq(expecting.title, details.title,
"expected value from getTitle");
browser.test.assertEq(expecting.popup, details.popup,
"expected value from getPopup");
finish();
});
} else {
finish();
}
});
}
function runTests() {
tabs = [];
tests = allTests.slice();
browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
tabs[0] = resultTabs[0].id;
nextTest();
});
}
browser.test.onMessage.addListener((msg) => {
if (msg == "runTests") {
runTests();
} else if (msg == "runNextTest") {
nextTest();
} else {
browser.test.fail(`Unexpected message: ${msg}`);
}
});
runTests();
},
});
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let currentWindow = window;
let windows = [];
function checkDetails(details) {
let image = currentWindow.document.getElementById(pageActionId);
if (details == null) {
ok(image == null || image.hidden, "image is hidden");
} else {
ok(image, "image exists");
is(image.src, details.icon, "icon URL is correct");
is(image.getAttribute("tooltiptext"), details.title, "image title is correct");
is(image.getAttribute("aria-label"), details.title, "image aria-label is correct");
// TODO: Popup URL.
}
}
let testNewWindows = 1;
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else if (testNewWindows) {
testNewWindows--;
BrowserTestUtils.openNewBrowserWindow().then(window => {
windows.push(window);
currentWindow = window;
return focusWindow(window);
}).then(() => {
extension.sendMessage("runTests");
});
} else {
resolve();
}
});
});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
currentWindow = null;
for (let win of windows.splice(0)) {
node = win.document.getElementById(pageActionId);
is(node, null, "pageAction image removed from second document");
yield BrowserTestUtils.closeWindow(win);
}
});
add_task(function* testDefaultTitle() {
yield runTests({
manifest: {
"name": "Foo Extension",
"page_action": {
"default_icon": "icon.png",
},
"permissions": ["tabs"],
},
getTests(tabs) {
let details = [
{ "title": "Foo Extension",
"popup": "",
"icon": browser.runtime.getURL("icon.png") },
{ "title": "Foo Title",
"popup": "",
"icon": browser.runtime.getURL("icon.png") },
];
return [
expect => {
browser.test.log("Initial state. No icon visible.");
expect(null);
},
expect => {
browser.test.log("Show the icon on the first tab, expect extension title as default title.");
browser.pageAction.show(tabs[0]);
expect(details[0]);
},
expect => {
browser.test.log("Change the title. Expect new title.");
browser.pageAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
expect(details[1]);
},
expect => {
browser.test.log("Clear the title. Expect extension title.");
browser.pageAction.setTitle({ tabId: tabs[0], title: "" });
expect(details[0]);
},
];
},
});
});

View File

@ -2,21 +2,9 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.popupOpen) {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
add_task(function* testPageActionPopup() {
let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head></html>`;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
@ -28,17 +16,17 @@ add_task(function* testPageActionPopup() {
},
files: {
"popup-a.html": `<script src="popup-a.js"></script>`,
"popup-a.html": scriptPage("popup-a.js"),
"popup-a.js": function() {
browser.runtime.sendMessage("from-popup-a");
},
"data/popup-b.html": `<script src="popup-b.js"></script>`,
"data/popup-b.html": scriptPage("popup-b.js"),
"data/popup-b.js": function() {
browser.runtime.sendMessage("from-popup-b");
},
"data/background.html": `<script src="background.js"></script>`,
"data/background.html": scriptPage("background.js"),
"data/background.js": function() {
let tabId;
@ -202,3 +190,5 @@ add_task(function* testPageActionSecurity() {
SimpleTest.endMonitorConsole();
yield waitForConsole;
});
add_task(forceGC);

View File

@ -42,19 +42,6 @@ add_task(function* testPageActionPopup() {
},
});
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
function openPopup(buttonId) {
let button = document.getElementById(buttonId);
if (buttonId == pageActionId) {
// TODO: I don't know why a proper synthesized event doesn't work here.
button.dispatchEvent(new MouseEvent("click", {}));
} else {
EventUtils.synthesizeMouseAtCenter(button, {}, window);
}
}
let promiseConsoleMessage = pattern => new Promise(resolve => {
Services.console.registerListener(function listener(msg) {
if (pattern.test(msg.message)) {
@ -72,21 +59,25 @@ add_task(function* testPageActionPopup() {
// BrowserAction:
let awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: BrowserAction/);
SimpleTest.expectUncaughtException();
openPopup(browserActionId);
yield clickBrowserAction(extension);
let message = yield awaitMessage;
ok(message.includes("WebExt Privilege Escalation: BrowserAction: typeof(browser) = undefined"),
`No BrowserAction API injection`);
yield closeBrowserAction(extension);
// PageAction
awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: PageAction/);
SimpleTest.expectUncaughtException();
openPopup(pageActionId);
yield clickPageAction(extension);
message = yield awaitMessage;
ok(message.includes("WebExt Privilege Escalation: PageAction: typeof(browser) = undefined"),
`No PageAction API injection: ${message}`);
yield closePageAction(extension);
SimpleTest.expectUncaughtException(false);
@ -95,12 +86,13 @@ add_task(function* testPageActionPopup() {
yield extension.awaitMessage("ok");
// Check that unprivileged documents don't get the API.
openPopup(browserActionId);
yield clickBrowserAction(extension);
yield extension.awaitMessage("from-popup-a");
yield closeBrowserAction(extension);
openPopup(pageActionId);
yield clickPageAction(extension);
yield extension.awaitMessage("from-popup-b");
yield closePageAction(extension);
yield extension.unload();
});

View File

@ -110,3 +110,5 @@ add_task(function* testBadPermissions() {
// new page, and no longer matches our expected state. This involves
// intentionally trying to trigger a race condition, and is probably not
// even worth attempting until we have proper |executeScript| callbacks.
add_task(forceGC);

View File

@ -48,6 +48,11 @@ function* testHasPermission(params) {
extension.sendMessage("execute-script");
yield extension.awaitFinish("executeScript");
if (params.tearDown) {
yield params.tearDown(extension);
}
yield extension.unload();
}
@ -82,6 +87,7 @@ add_task(function* testGoodPermissions() {
return Promise.resolve();
},
setup: clickBrowserAction,
tearDown: closeBrowserAction,
});
info("Test activeTab permission with a page action click");
@ -99,6 +105,7 @@ add_task(function* testGoodPermissions() {
});
},
setup: clickPageAction,
tearDown: closePageAction,
});
info("Test activeTab permission with a browser action w/popup click");
@ -108,6 +115,7 @@ add_task(function* testGoodPermissions() {
"browser_action": { "default_popup": "_blank.html" },
},
setup: clickBrowserAction,
tearDown: closeBrowserAction,
});
info("Test activeTab permission with a page action w/popup click");
@ -125,6 +133,7 @@ add_task(function* testGoodPermissions() {
});
},
setup: clickPageAction,
tearDown: closePageAction,
});
info("Test activeTab permission with a context menu click");
@ -155,3 +164,5 @@ add_task(function* testGoodPermissions() {
yield BrowserTestUtils.removeTab(tab);
});
add_task(forceGC);

View File

@ -63,6 +63,7 @@ add_task(function* () {
clickBrowserAction(extension);
yield extension.awaitMessage("popup-finished");
yield closeBrowserAction(extension);
yield extension.unload();
});

View File

@ -172,3 +172,5 @@ add_task(function* test_url() {
});
});
});
add_task(forceGC);

View File

@ -2,10 +2,27 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported CustomizableUI makeWidgetId focusWindow clickBrowserAction clickPageAction */
/* exported CustomizableUI makeWidgetId focusWindow forceGC
* getBrowserActionWidget
* clickBrowserAction clickPageAction
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown
*/
var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
// Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
// times in debug builds, which results in intermittent timeouts. Until we have
// a better solution, we force a GC after certain strategic tests, which tend to
// accumulate a high number of unreaped windows.
function forceGC() {
if (AppConstants.DEBUG) {
Cu.forceGC();
}
}
function makeWidgetId(id) {
id = id.toLowerCase();
return id.replace(/[^a-z0-9_-]/g, "_");
@ -28,12 +45,58 @@ var focusWindow = Task.async(function* focusWindow(win) {
yield promise;
});
function clickBrowserAction(extension, win = window) {
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let elem = win.document.getElementById(browserActionId);
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.state == "open") {
resolve();
} else {
let onPopupShown = event => {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
};
popup.addEventListener("popupshown", onPopupShown);
}
});
}
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
return new Promise(SimpleTest.executeSoon);
function getBrowserActionWidget(extension) {
return CustomizableUI.getWidget(makeWidgetId(extension.id) + "-browser-action");
}
function getBrowserActionPopup(extension, win = window) {
let group = getBrowserActionWidget(extension);
if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
return win.document.getElementById("customizationui-widget-panel");
}
return null;
}
var clickBrowserAction = Task.async(function* (extension, win = window) {
let group = getBrowserActionWidget(extension);
let widget = group.forWindow(win);
if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
ok(!widget.overflowed, "Expect widget not to be overflowed");
} else if (group.areaType == CustomizableUI.TYPE_MENU_PANEL) {
yield win.PanelUI.show();
}
EventUtils.synthesizeMouseAtCenter(widget.node, {}, win);
});
function closeBrowserAction(extension, win = window) {
let group = getBrowserActionWidget(extension);
let node = win.document.getElementById(group.viewId);
CustomizableUI.hidePanelForNode(node);
return Promise.resolve();
}
function getPageActionPopup(extension, win = window) {
let panelId = makeWidgetId(extension.id) + "-panel";
return win.document.getElementById(panelId);
}
function clickPageAction(extension, win = window) {
@ -52,3 +115,13 @@ function clickPageAction(extension, win = window) {
EventUtils.synthesizeMouseAtCenter(elem, {}, win);
return new Promise(SimpleTest.executeSoon);
}
function closePageAction(extension, win = window) {
let node = getPageActionPopup(extension, win);
if (node) {
node.hidePopup();
}
return Promise.resolve();
}

View File

@ -12,6 +12,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
@ -660,9 +662,7 @@ nsBrowserContentHandler.prototype = {
var url = Services.urlFormatter.formatURLPref("app.support.baseURL") +
"win10-default-browser";
if (urlParam == url) {
var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
.getService(Components.interfaces.nsIShellService);
isDefault = shellSvc.isDefaultBrowser(false, false);
isDefault = ShellService.isDefaultBrowser(false, false);
}
} catch (ex) {}
if (isDefault) {

View File

@ -140,16 +140,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
"resource:///modules/ContentCrashHandlers.jsm");
#endif
XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
try {
return Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
}
catch(ex) {
return null;
}
});
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle('chrome://branding/locale/brand.properties');
});
@ -180,6 +170,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");

View File

@ -5,6 +5,7 @@
Components.utils.import("resource://gre/modules/Downloads.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
Components.utils.import("resource:///modules/ShellService.jsm");
Components.utils.import("resource:///modules/TransientPrefs.jsm");
#ifdef E10S_TESTING_ONLY
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",

View File

@ -0,0 +1,86 @@
/* 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 = ["ShellService"];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/**
* Internal functionality to save and restore the docShell.allow* properties.
*/
let ShellServiceInternal = {
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
get canSetDesktopBackground() {
if (AppConstants.platform == "win" ||
AppConstants.platform == "macosx") {
return true;
}
if (AppConstants.platform == "linux") {
if (this.shellService) {
let linuxShellService = this.shellService
.QueryInterface(Ci.nsIGNOMEShellService);
return linuxShellService.canSetDesktopBackground;
}
}
return false;
},
/**
* Used to determine whether or not to show a "Set Default Browser"
* query dialog. This attribute is true if the application is starting
* up and "browser.shell.checkDefaultBrowser" is true, otherwise it
* is false.
*/
_checkedThisSession: false,
get shouldCheckDefaultBrowser() {
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (this._checkedThisSession) {
return false;
}
return Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
},
set shouldCheckDefaultBrowser(shouldCheck) {
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
},
isDefaultBrowser(startupCheck, forAllTypes) {
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (startupCheck) {
this._checkedThisSession = true;
}
if (this.shellService) {
return this.shellService.isDefaultBrowser(startupCheck, forAllTypes);
}
}
};
XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
"@mozilla.org/browser/shell-service;1", Ci.nsIShellService);
/**
* The external API exported by this module.
*/
this.ShellService = new Proxy(ShellServiceInternal, {
get(target, name) {
return name in target ? target[name] :
target.shellService[name];
}
});

View File

@ -21,6 +21,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
XPIDL_SOURCES += [
'nsIMacShellService.idl',
]
elif CONFIG['MOZ_WIDGET_GTK']:
XPIDL_SOURCES += [
'nsIGNOMEShellService.idl',
]
XPIDL_MODULE = 'shellservice'
@ -45,6 +49,10 @@ EXTRA_COMPONENTS += [
'nsSetDefaultBrowser.manifest',
]
EXTRA_JS_MODULES += [
'ShellService.jsm',
]
for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'):
DEFINES[var] = '"%s"' % CONFIG[var]

View File

@ -117,7 +117,7 @@ nsGNOMEShellService::Init()
return appPath->GetNativePath(mAppPath);
}
NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIShellService)
NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService)
bool
nsGNOMEShellService::GetAppPathFromLauncher()
@ -201,8 +201,6 @@ nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck,
bool* aIsDefaultBrowser)
{
*aIsDefaultBrowser = false;
if (aStartupCheck)
mCheckedThisSession = true;
nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
@ -325,37 +323,6 @@ nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes,
return NS_OK;
}
NS_IMETHODIMP
nsGNOMEShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsGNOMEShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
NS_IMETHODIMP
nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult)
{

View File

@ -6,17 +6,18 @@
#ifndef nsgnomeshellservice_h____
#define nsgnomeshellservice_h____
#include "nsIShellService.h"
#include "nsIGNOMEShellService.h"
#include "nsStringAPI.h"
#include "mozilla/Attributes.h"
class nsGNOMEShellService final : public nsIShellService
class nsGNOMEShellService final : public nsIGNOMEShellService
{
public:
nsGNOMEShellService() : mCheckedThisSession(false), mAppIsInPath(false) { }
nsGNOMEShellService() : mAppIsInPath(false) { }
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
NS_DECL_NSIGNOMESHELLSERVICE
nsresult Init();
@ -27,7 +28,6 @@ private:
bool CheckHandlerMatchesAppName(const nsACString& handler) const;
bool GetAppPathFromLauncher();
bool mCheckedThisSession;
bool mUseLocaleFilenames;
nsCString mAppPath;
bool mAppIsInPath;

View File

@ -0,0 +1,19 @@
/* 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 "nsIShellService.idl"
[scriptable, uuid(2ce5c803-edcd-443d-98eb-ceba86d02d13)]
interface nsIGNOMEShellService : nsIShellService
{
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
readonly attribute boolean canSetDesktopBackground;
};

View File

@ -5,7 +5,7 @@
#include "nsIShellService.idl"
[scriptable, uuid(291a27cd-ef4c-46c6-a2f8-83182498167e)]
[scriptable, uuid(387fdc80-0077-4b60-a0d9-d9e80a83ba64)]
interface nsIMacShellService : nsIShellService
{
const long APPLICATION_KEYCHAIN_ACCESS = 2;

View File

@ -8,7 +8,7 @@
interface nsIDOMElement;
interface nsIFile;
[scriptable, uuid(53f4bc4a-5b86-4643-8e67-4907ecbab34c)]
[scriptable, uuid(2d1a95e4-5bd8-4eeb-b0a8-c1455fd2a357)]
interface nsIShellService : nsISupports
{
/**
@ -38,23 +38,6 @@ interface nsIShellService : nsISupports
*/
void setDefaultBrowser(in boolean aClaimAllTypes, in boolean aForAllUsers);
/**
* Used to determine whether or not to show a "Set Default Browser"
* query dialog. This attribute is true if the application is starting
* up and "browser.shell.checkDefaultBrowser" is true, otherwise it
* is false.
*/
attribute boolean shouldCheckDefaultBrowser;
/**
* Used to determine whether or not to offer "Set as desktop background"
* functionality. Even if shell service is available it is not
* guaranteed that it is able to set the background for every desktop
* which is especially true for Linux with its many different desktop
* environments.
*/
readonly attribute boolean canSetDesktopBackground;
/**
* Flags for positioning/sizing of the Desktop Background image.
*/

View File

@ -5,7 +5,7 @@
#include "nsIShellService.idl"
[scriptable, uuid(13f20725-4fd5-431f-90a1-525ab31755b1)]
[scriptable, uuid(f8a26b94-49e5-4441-8fbc-315e0b4f22ef)]
interface nsIWindowsShellService : nsIShellService
{
/**

View File

@ -57,12 +57,6 @@ nsMacShellService::IsDefaultBrowser(bool aStartupCheck,
::CFRelease(defaultBrowserID);
}
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = true;
return NS_OK;
}
@ -103,44 +97,6 @@ nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
NS_IMETHODIMP
nsMacShellService::GetCanSetDesktopBackground(bool* aResult)
{
*aResult = true;
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement,
int32_t aPosition)

View File

@ -15,7 +15,7 @@ class nsMacShellService : public nsIMacShellService,
public nsIWebProgressListener
{
public:
nsMacShellService() : mCheckedThisSession(false) {};
nsMacShellService() {};
NS_DECL_ISUPPORTS
NS_DECL_NSISHELLSERVICE
@ -27,8 +27,6 @@ protected:
private:
nsCOMPtr<nsIFile> mBackgroundFile;
bool mCheckedThisSession;
};
#endif // nsmacshellservice_h____

View File

@ -2,13 +2,14 @@
* 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/. */
/*
/*
* --setDefaultBrowser commandline handler
* Makes the current executable the "default browser".
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource:///modules/ShellService.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function nsSetDefaultBrowser() {}
@ -16,9 +17,7 @@ function nsSetDefaultBrowser() {}
nsSetDefaultBrowser.prototype = {
handle: function nsSetDefault_handle(aCmdline) {
if (aCmdline.handleFlag("setDefaultBrowser", false)) {
var shell = Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
shell.setDefaultBrowser(true, true);
ShellService.setDefaultBrowser(true, true);
}
},

View File

@ -608,12 +608,6 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
bool aForAllTypes,
bool* aIsDefaultBrowser)
{
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = true;
// Assume we're the default unless one of the several checks below tell us
// otherwise.
*aIsDefaultBrowser = true;
@ -807,13 +801,6 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetCanSetDesktopBackground(bool* aResult)
{
*aResult = true;
return NS_OK;
}
static nsresult
DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
{
@ -984,39 +971,6 @@ nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::GetShouldCheckDefaultBrowser(bool* aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = false;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
}
NS_IMETHODIMP
nsWindowsShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return rv;
}
return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
}
static nsresult
WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{
@ -1358,8 +1312,7 @@ nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor)
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService() :
mCheckedThisSession(false)
nsWindowsShellService::nsWindowsShellService()
{
}

View File

@ -31,9 +31,6 @@ protected:
nsresult LaunchModernSettingsDialogDefaultApps();
nsresult InvokeHTTPOpenAsVerb();
nsresult LaunchHTTPHandlerPane();
private:
bool mCheckedThisSession;
};
#endif // nswindowsshellservice_h____

View File

@ -1,15 +1,7 @@
Components.utils.import("resource:///modules/ShellService.jsm");
function test() {
let osString = Cc["@mozilla.org/xre/app-info;1"].
getService(Ci.nsIXULRuntime).OS;
// this test is Linux-specific
if (osString != "Linux")
return;
let shell = Cc["@mozilla.org/browser/shell-service;1"].
getService(Ci.nsIShellService);
shell.setDefaultBrowser(true, false);
ok(shell.isDefaultBrowser(true, false), "we got here and are the default browser");
ok(shell.isDefaultBrowser(true, true), "we got here and are the default browser");
ShellService.setDefaultBrowser(true, false);
ok(ShellService.isDefaultBrowser(true, false), "we got here and are the default browser");
ok(ShellService.isDefaultBrowser(true, true), "we got here and are the default browser");
}

View File

@ -250,6 +250,11 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
max-width: @standaloneSubviewWidth@;
}
/* Give WebExtension stand-alone panels extra width for Chrome compatibility */
.cui-widget-panel[viewId^=PanelUI-webext-] .panel-mainview {
max-width: 800px;
}
panelview:not([mainview]) .toolbarbutton-text,
.cui-widget-panel toolbarbutton > .toolbarbutton-text {
text-align: start;

View File

@ -8,7 +8,7 @@
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&title;</title>
<title>&animationInspectorTitle;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css" type="text/css"/>

View File

@ -9,7 +9,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&title;</title>
<title>&fontInspectorTitle;</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/fonts.css" type="text/css"/>

View File

@ -10,7 +10,7 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&title;</title>
<title>&layoutViewTitle;</title>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"/>

View File

@ -11,8 +11,9 @@
tools. A good criteria is the language in which you'd find the best
documentation on web development on the web. -->
<!-- LOCALIZATION NOTE (title): This is the label shown in the sidebar tab -->
<!ENTITY title "Animations">
<!-- LOCALIZATION NOTE (animationInspectorTitle): This is the label shown in the
sidebar tab -->
<!ENTITY animationInspectorTitle "Animations">
<!-- LOCALIZATION NOTE (invalidElement): This is the label shown in the panel
when an invalid node is currently selected in the inspector. -->

View File

@ -5,7 +5,7 @@
<!-- LOCALIZATION NOTE : FILE This file contains the Font Inspector strings.
- The Font Inspector is the panel accessible in the Inspector sidebar. -->
<!ENTITY title "Fonts">
<!ENTITY fontInspectorTitle "Fonts">
<!ENTITY showAllFonts "See all the fonts used in the page">
<!ENTITY usedAs "Used as: ">
<!ENTITY system "system">

View File

@ -16,7 +16,7 @@
- The text appears on the bottom right corner of the layout view when
- the corresponding box is hovered. -->
<!ENTITY title "Box Model">
<!ENTITY layoutViewTitle "Box Model">
<!ENTITY margin.tooltip "margin">
<!ENTITY border.tooltip "border">
<!ENTITY padding.tooltip "padding">

View File

@ -1185,6 +1185,8 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
* grip. This is typically set to true when the object needs to be
* displayed in an array preview, or as a property value in object
* previews, etc.
* - shorten - boolean that tells the renderer to display a truncated
* grip.
* @return DOMElement
* The DOM element that displays the given grip.
*/
@ -1209,10 +1211,15 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
}
}
let unshortenedGrip = grip;
if (options.shorten) {
grip = this.shortenValueGrip(grip)
}
let result = this.document.createElementNS(XHTML_NS, "span");
if (isPrimitive) {
if (Widgets.URLString.prototype.containsURL.call(Widgets.URLString.prototype, grip)) {
let widget = new Widgets.URLString(this, grip, options).render();
let widget = new Widgets.URLString(this, grip, unshortenedGrip).render();
return widget.element;
}
@ -2199,11 +2206,14 @@ Widgets.MessageTimestamp.prototype = Heritage.extend(Widgets.BaseWidget.prototyp
* The owning message.
* @param string str
* The string, which contains at least one valid URL.
* @param string unshortenedStr
* The unshortened form of the string, if it was shortened.
*/
Widgets.URLString = function(message, str)
Widgets.URLString = function(message, str, unshortenedStr)
{
Widgets.BaseWidget.call(this, message);
this.str = str;
this.unshortenedStr = unshortenedStr;
};
Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
@ -2228,16 +2238,23 @@ Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
this.element.appendChild(this._renderText("\""));
// As we walk through the tokens of the source string, we make sure to preserve
// the original whitespace that seperated the tokens.
// the original whitespace that separated the tokens.
let tokens = this.str.split(/\s+/);
let textStart = 0;
let tokenStart;
for (let token of tokens) {
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
let unshortenedToken;
tokenStart = this.str.indexOf(token, textStart);
if (this._isURL(token)) {
// The last URL in the string might be shortened. If so, get the
// real URL so the rendered link can point to it.
if (i === tokens.length - 1 && this.unshortenedStr) {
unshortenedToken = this.unshortenedStr.slice(tokenStart).split(/\s+/, 1)[0];
}
this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
textStart = tokenStart + token.length;
this.element.appendChild(this._renderURL(token));
this.element.appendChild(this._renderURL(token, unshortenedToken));
}
}
@ -2289,15 +2306,18 @@ Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
*
* @param string url
* The string to be rendered as a url.
* @param string fullUrl
* The unshortened form of the URL, if it was shortened.
* @return DOMElement
* An element containing the rendered string.
*/
_renderURL: function(url)
_renderURL: function(url, fullUrl)
{
let unshortened = fullUrl || url;
let result = this.el("a", {
class: "url",
title: url,
href: url,
title: unshortened,
href: unshortened,
draggable: false
}, url);
this.message._addLinkCallback(result);
@ -2414,8 +2434,7 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
if (valueIsText) {
this._text(value);
} else {
let shortVal = this.message.shortenValueGrip(value);
let valueElem = this.message._renderValueGrip(shortVal, { concise: true });
let valueElem = this.message._renderValueGrip(value, { concise: true, shorten: true });
container.appendChild(valueElem);
}
},
@ -2808,8 +2827,7 @@ Widgets.ObjectRenderers.add({
emptySlots = 0;
}
let shortVal = this.message.shortenValueGrip(item);
let elem = this.message._renderValueGrip(shortVal, { concise: true });
let elem = this.message._renderValueGrip(item, { concise: true, shorten: true });
this.element.appendChild(elem);
}
}

View File

@ -73,6 +73,27 @@ var inputTests = [
output: "foo://example.com",
},
// 9: Shortened URL in an array
{
input: "['http://example.com/abcdefghijabcdefghij some other text']",
output: "Array [ \"http://example.com/abcdefghijabcdef\u2026\" ]",
printOutput: "http://example.com/abcdefghijabcdefghij some other text",
expectedTab: "http://example.com/abcdefghijabcdefghij",
getClickableNode: (msg) => msg.querySelectorAll("a")[1],
},
// 10: Shortened URL in an object
{
input: "{test: 'http://example.com/abcdefghijabcdefghij some other text'}",
output: "Object { test: \"http://example.com/abcdefghijabcdef\u2026\" }",
printOutput: "[object Object]",
evalOutput: "http://example.com/abcdefghijabcdefghij some other text",
noClick: true,
consoleLogClick: true,
expectedTab: "http://example.com/abcdefghijabcdefghij",
getClickableNode: (msg) => msg.querySelectorAll("a")[1],
},
];
function test() {

View File

@ -1454,8 +1454,13 @@ function checkOutputForInputs(hud, inputTests) {
}],
});
let msg = [...result.matched][0];
if (entry.consoleLogClick) {
yield checkObjectClick(entry, msg);
}
if (typeof entry.inspectorIcon == "boolean") {
let msg = [...result.matched][0];
info("Checking Inspector Link: " + entry.input);
yield checkLinkToInspector(entry.inspectorIcon, msg);
}
@ -1483,11 +1488,13 @@ function checkOutputForInputs(hud, inputTests) {
hud.jsterm.clearOutput();
hud.jsterm.execute(entry.input);
let evalOutput = entry.evalOutput || entry.output;
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "JS eval output: " + entry.output,
text: entry.output,
name: "JS eval output: " + entry.evalOutput,
text: entry.evalOutput,
category: CATEGORY_OUTPUT,
}],
});
@ -1504,8 +1511,13 @@ function checkOutputForInputs(hud, inputTests) {
function* checkObjectClick(entry, msg) {
info("Clicking: " + entry.input);
let body = msg.querySelector(".message-body a") ||
msg.querySelector(".message-body");
let body;
if (entry.getClickableNode) {
body = entry.getClickableNode(msg);
} else {
body = msg.querySelector(".message-body a") ||
msg.querySelector(".message-body");
}
ok(body, "the message body");
let deferredVariablesView = promise.defer();

View File

@ -21,15 +21,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
.getService(Components.interfaces.nsIAppShellService);
ok(appShellService, "Should be able to get app shell service");
var webNavigation = appShellService.createWindowlessBrowser();
ok(webNavigation, "Should be able to create windowless browser");
var windowlessBrowser = appShellService.createWindowlessBrowser();
ok(windowlessBrowser, "Should be able to create windowless browser");
var interfaceRequestor = webNavigation.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
ok(windowlessBrowser instanceof Components.interfaces.nsIWindowlessBrowser,
"Windowless browser should implement nsIWindowlessBrowser");
var webNavigation = windowlessBrowser.QueryInterface(Components.interfaces.nsIWebNavigation);
ok(webNavigation, "Windowless browser should implement nsIWebNavigation");
var interfaceRequestor = windowlessBrowser.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
ok(interfaceRequestor, "Should be able to query interface requestor interface");
var docShell = interfaceRequestor.getInterface(Components.interfaces.nsIDocShell);
ok(docShell, "Should be able to get doc shell interface");
var document = webNavigation.document;
ok(document, "Should be able to get document");
@ -55,12 +61,30 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=846906
is(rect.width, 1024);
is(rect.height, 768);
windowlessBrowser.close();
// Once the browser is closed, nsIWebNavigation and
// nsIInterfaceRequestor methods should no longer be accessible.
try {
windowlessBrowser.getInterface(Components.interfaces.nsIDocShell);
ok(false);
} catch (e) {
is(e.result, Components.results.NS_ERROR_NULL_POINTER);
}
try {
windowlessBrowser.document;
ok(false);
} catch (e) {
is(e.result, Components.results.NS_ERROR_NULL_POINTER);
}
SimpleTest.finish();
};
iframe.setAttribute("src", "http://mochi.test:8888/chrome/docshell/test/chrome/bug846906.html");
};
document.documentElement.appendChild(iframe);
]]>
</script>

View File

@ -981,6 +981,23 @@ Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
}
}
nsresult
Notification::Init()
{
if (!mWorkerPrivate) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
nsresult rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
NS_ENSURE_SUCCESS(rv, rv);
rv = obs->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
void
Notification::SetAlertName()
{
@ -1150,6 +1167,7 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal,
const nsAString& aTitle,
const NotificationOptions& aOptions)
{
nsresult rv;
nsString id;
if (!aID.IsEmpty()) {
id = aID;
@ -1158,7 +1176,7 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal,
do_GetService("@mozilla.org/uuid-generator;1");
NS_ENSURE_TRUE(uuidgen, nullptr);
nsID uuid;
nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
rv = uuidgen->GenerateUUIDInPlace(&uuid);
NS_ENSURE_SUCCESS(rv, nullptr);
char buffer[NSID_LENGTH];
@ -1174,6 +1192,8 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal,
aOptions.mTag,
aOptions.mIcon,
aOptions.mMozbehavior);
rv = notification->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
return notification.forget();
}
@ -1202,6 +1222,8 @@ NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
nsIPrincipal*
@ -2722,6 +2744,46 @@ Notification::OpenSettings(nsIPrincipal* aPrincipal)
return NS_OK;
}
NS_IMETHODIMP
Notification::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
AssertIsOnMainThread();
if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) ||
!strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC)) {
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
if (SameCOMIdentity(aSubject, window)) {
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
obs->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
}
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
nsCOMPtr<nsIPrincipal> nodePrincipal = doc ? doc->NodePrincipal() :
nullptr;
if (nodePrincipal) {
appStatus = nodePrincipal->GetAppStatus();
appId = nodePrincipal->GetAppId();
}
if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED ||
appId == nsIScriptSecurityManager::NO_APP_ID ||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
CloseInternal();
}
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -17,6 +17,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsTHashtable.h"
#include "nsWeakReference.h"
#define NOTIFICATIONTELEMETRYSERVICE_CONTRACTID \
"@mozilla.org/notificationTelemetryService;1"
@ -132,6 +133,8 @@ private:
*
*/
class Notification : public DOMEventTargetHelper
, public nsIObserver
, public nsSupportsWeakReference
{
friend class CloseNotificationRunnable;
friend class NotificationTask;
@ -151,6 +154,7 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper)
NS_DECL_NSIOBSERVER
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
// Returns if Notification.get() is allowed for the current global.
@ -325,6 +329,7 @@ protected:
const nsAString& aTitle,
const NotificationOptions& aOptions);
nsresult Init();
bool IsInPrivateBrowsing();
void ShowInternal();
void CloseInternal();

View File

@ -40,4 +40,6 @@ add_task(function* test_windowlessBrowserTroubleshootCrash() {
});
ok(data.graphics.windowLayerManagerType !== "None", "windowless browser window should not set windowLayerManagerType to 'None'");
webNav.close();
});

View File

@ -574,8 +574,10 @@ pref("layers.async-video.enabled", true);
pref("layers.async-pan-zoom.enabled", true);
// APZ prefs that are different from B2G
pref("apz.allow_immediate_handoff", false);
pref("apz.touch_start_tolerance", "0.06");
// APZ physics settings, copied from B2G
pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking
// APZ physics settings reviewed by UX
pref("apz.fling_curve_function_x1", "0.41");
pref("apz.fling_curve_function_y1", "0.0");
pref("apz.fling_curve_function_x2", "0.80");
@ -584,6 +586,7 @@ pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
pref("apz.fling_friction", "0.004");
pref("apz.fling_stopped_threshold", "0.1");
pref("apz.max_velocity_inches_per_ms", "0.07");
pref("apz.fling_accel_interval_ms", 750);
#endif
pref("layers.progressive-paint", true);

View File

@ -780,7 +780,6 @@ sync_java_files = [TOPSRCDIR + '/mobile/android/services/src/main/java/org/mozil
'background/common/log/writers/ThreadLocalTagLogWriter.java',
'background/common/PrefsBranch.java',
'background/common/telemetry/TelemetryWrapper.java',
'background/datareporting/TelemetryRecorder.java',
'background/db/CursorDumper.java',
'background/db/Tab.java',
'background/fxa/FxAccount10AuthDelegate.java',

View File

@ -1124,18 +1124,22 @@ public class BrowserSearch extends HomeFragment
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
mAdapter.swapCursor(c);
if (mAdapter != null) {
mAdapter.swapCursor(c);
// We should handle autocompletion based on the search term
// associated with the loader that has just provided
// the results.
SearchCursorLoader searchLoader = (SearchCursorLoader) loader;
handleAutocomplete(searchLoader.getSearchTerm(), c);
// We should handle autocompletion based on the search term
// associated with the loader that has just provided
// the results.
SearchCursorLoader searchLoader = (SearchCursorLoader) loader;
handleAutocomplete(searchLoader.getSearchTerm(), c);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
if (mAdapter != null) {
mAdapter.swapCursor(null);
}
}
}

View File

@ -1,316 +0,0 @@
/* 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/. */
package org.mozilla.gecko.background.datareporting;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.json.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import android.util.Base64;
/**
* Writes telemetry ping to file.
*
* Also creates and updates a SHA-256 checksum for the payload to be included in the ping
* file.
*
* A saved telemetry ping file consists of JSON in the following format,
* {
* "slug": "<uuid-string>",
* "payload": "<escaped-json-data-string>",
* "checksum": "<base64-sha-256-string>"
* }
*
* This class writes first to a temporary file and then, after finishing the contents of the ping,
* moves that to the file specified by the caller. This is to avoid uploads of partially written
* ping files.
*
* The API provided by this class:
* startPingFile() - opens stream to a tmp File in the Android cache directory and writes the slug header
* appendPayload(String payloadContent) - appends to the payload of the ping and updates the checksum
* finishPingFile() - writes the checksum to the tmp file and moves it to the File specified by the caller.
*
* In the case of errors, we try to close the stream and File.
*/
public class TelemetryRecorder {
private final String LOG_TAG = "TelemetryRecorder";
private final File parentDir;
private final String filename;
private File tmpFile;
private File destFile;
private final File cacheDir;
private OutputStream outputStream;
private MessageDigest checksum;
private String base64Checksum;
/**
* Charset to use for writing pings; default is us-ascii.
*
* When telemetry calculates the checksum for the ping file, it lossily
* converts utf-16 to ascii. Therefore we have to treat characters in the
* traces as ascii rather than say utf-8. Otherwise we will get a "wrong"
* checksum.
*/
private String charset = "us-ascii";
/**
* Override blockSize in constructor if desired.
* Default block size is that of BufferedOutputStream.
*/
private int blockSize = 0;
/**
* Constructs a TelemetryRecorder for writing a ping file. A temporary file will be written first,
* and then moved to the destination file location specified by the caller.
*
* The directory for writing the temporary file is highly suggested to be the Android internal cache directory,
* fetched by <code>context.getCacheDir()</code>
*
* If the destination file already exists, it will be deleted and overwritten.
*
* Default charset: "us-ascii"
* Default block size: uses constructor default of 8192 bytes (see javadocs for
* <code>BufferedOutputStream</code>
* @param parentPath
* path of parent directory of ping file to be written
* @param cacheDir
* path of cache directory for writing temporary files.
* @param filename
* name of ping file to be written
*/
public TelemetryRecorder(File parentDir, File cacheDir, String filename) {
if (!parentDir.isDirectory()) {
throw new IllegalArgumentException("Expecting directory, got non-directory File instead.");
}
this.parentDir = parentDir;
this.filename = filename;
this.cacheDir = cacheDir;
}
public TelemetryRecorder(File parentDir, File cacheDir, String filename, String charset) {
this(parentDir, cacheDir, filename);
this.charset = charset;
}
public TelemetryRecorder(File parentDir, File cacheDir, String filename, String charset, int blockSize) {
this(parentDir, cacheDir, filename, charset);
this.blockSize = blockSize;
}
/**
* Start the temporary ping file and write the "slug" header and payload key, of the
* format:
*
* { "slug": "< filename >", "payload":
*
* @throws Exception
* Checked exceptions <code>NoSuchAlgorithmException</code>,
* <code>UnsupportedEncodingException</code>, or
* <code>IOException</code> and unchecked exception that
* are rethrown to caller
*/
public void startPingFile() throws Exception {
// Open stream to temporary file for writing.
try {
tmpFile = File.createTempFile(filename, "tmp", cacheDir);
} catch (IOException e) {
// Try to create the temporary file in the ping directory.
tmpFile = new File(parentDir, filename + ".tmp");
try {
tmpFile.createNewFile();
} catch (IOException e1) {
cleanUpAndRethrow("Failed to create tmp file in temp directory or ping directory.", e1);
}
}
try {
if (blockSize > 0) {
outputStream = new BufferedOutputStream(new FileOutputStream(tmpFile), blockSize);
} else {
outputStream = new BufferedOutputStream(new FileOutputStream(tmpFile));
}
// Create checksum for ping.
checksum = MessageDigest.getInstance("SHA-256");
// Write ping header.
byte[] header = makePingHeader(filename);
outputStream.write(header);
Logger.debug(LOG_TAG, "Wrote " + header.length + " header bytes.");
} catch (NoSuchAlgorithmException e) {
cleanUpAndRethrow("Error creating checksum digest", e);
} catch (UnsupportedEncodingException e) {
cleanUpAndRethrow("Error writing header", e);
} catch (IOException e) {
cleanUpAndRethrow("Error writing to stream", e);
}
}
private byte[] makePingHeader(String slug)
throws UnsupportedEncodingException {
return ("{\"slug\":" + JSONObject.quote(slug) + "," + "\"payload\":\"")
.getBytes(charset);
}
/**
* Append payloadContent to ping file and update the checksum.
*
* @param payloadContent
* String content to be written
* @return number of bytes written, or -1 if writing failed
* @throws Exception
* Checked exceptions <code>UnsupportedEncodingException</code> or
* <code>IOException</code> and unchecked exception that
* are rethrown to caller
*/
public int appendPayload(String payloadContent) throws Exception {
if (payloadContent == null) {
cleanUpAndRethrow("Payload is null", new Exception());
return -1;
}
try {
byte[] payloadBytes = payloadContent.getBytes(charset);
// If we run into an error, we'll throw and abort, so checksum won't be stale.
checksum.update(payloadBytes);
byte[] quotedPayloadBytes = JSONObject.quote(payloadContent).getBytes(charset);
// First and last bytes are quotes inserted by JSONObject.quote; discard
// them.
int numBytes = quotedPayloadBytes.length - 2;
outputStream.write(quotedPayloadBytes, 1, numBytes);
return numBytes;
} catch (UnsupportedEncodingException e) {
cleanUpAndRethrow("Error encoding payload", e);
return -1;
} catch (IOException e) {
cleanUpAndRethrow("Error writing to stream", e);
return -1;
}
}
/**
* Add the checksum of the payload to the ping file and close the stream.
*
* @throws Exception
* Checked exceptions <code>UnsupportedEncodingException</code> or
* <code>IOException</code> and unchecked exception that
* are rethrown to caller
*/
public void finishPingFile() throws Exception {
try {
byte[] footer = makePingFooter(checksum);
outputStream.write(footer);
// We're done writing, so force the stream to flush the buffer.
outputStream.flush();
Logger.debug(LOG_TAG, "Wrote " + footer.length + " footer bytes.");
} catch (UnsupportedEncodingException e) {
cleanUpAndRethrow("Checksum encoding exception", e);
} catch (IOException e) {
cleanUpAndRethrow("Error writing footer to stream", e);
} finally {
try {
outputStream.close();
} catch (IOException e) {
// Failed to close, nothing we can do except discard the reference to the stream.
outputStream = null;
}
}
// Move temp file to destination specified by caller.
try {
File destFile = new File(parentDir, filename);
// Delete file if it exists - docs state that rename may fail if the File already exists.
if (destFile.exists()) {
destFile.delete();
}
boolean result = tmpFile.renameTo(destFile);
if (!result) {
throw new IOException("Could not move tmp file to destination.");
}
} finally {
cleanUp();
}
}
private byte[] makePingFooter(MessageDigest checksum)
throws UnsupportedEncodingException {
base64Checksum = Base64.encodeToString(checksum.digest(), Base64.NO_WRAP);
return ("\",\"checksum\":" + JSONObject.quote(base64Checksum) + "}")
.getBytes(charset);
}
/**
* Get final digested Base64 checksum.
*
* @return String checksum if it has been calculated, null otherwise.
*/
protected String getFinalChecksum() {
return base64Checksum;
}
public String getCharset() {
return this.charset;
}
/**
* Clean up checksum and delete the temporary file.
*/
private void cleanUp() {
// Discard checksum.
checksum.reset();
// Clean up files.
if (tmpFile != null && tmpFile.exists()) {
tmpFile.delete();
}
tmpFile = null;
}
/**
* Log message and error and clean up, then rethrow exception to caller.
*
* @param message
* Error message
* @param e
* Exception
*
* @throws Exception
* Exception to be rethrown to caller
*/
private void cleanUpAndRethrow(String message, Exception e) throws Exception {
Logger.error(LOG_TAG, message, e);
cleanUp();
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException exception) {
// Failed to close stream; nothing we can do, and we're aborting anyways.
}
}
if (destFile != null && destFile.exists()) {
destFile.delete();
}
// Rethrow the exception.
throw e;
}
}

View File

@ -76,7 +76,6 @@ background_junit3_sources = [
'src/org/mozilla/gecko/background/sync/TestTabsRecord.java',
'src/org/mozilla/gecko/background/sync/TestUpgradeRequired.java',
'src/org/mozilla/gecko/background/sync/TestWebURLFinder.java',
'src/org/mozilla/gecko/background/telemetry/TestTelemetryRecorder.java',
'src/org/mozilla/gecko/background/testhelpers/BaseMockServerSyncStage.java',
'src/org/mozilla/gecko/background/testhelpers/CommandHelpers.java',
'src/org/mozilla/gecko/background/testhelpers/DefaultGlobalSessionCallback.java',

View File

@ -32,4 +32,3 @@ subsuite = background
[src/org/mozilla/gecko/background/sync/TestTabsRecord.java]
[src/org/mozilla/gecko/background/sync/TestUpgradeRequired.java]
[src/org/mozilla/gecko/background/sync/TestWebURLFinder.java]
[src/org/mozilla/gecko/background/telemetry/TestTelemetryRecorder.java]

View File

@ -1,166 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.background.telemetry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import junit.framework.Assert;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.background.datareporting.TelemetryRecorder;
import org.mozilla.gecko.background.helpers.FakeProfileTestCase;
import android.util.Base64;
public class TestTelemetryRecorder extends FakeProfileTestCase {
private TelemetryRecorder telemetryRecorder;
private File TelemetryControllerDir;
private File cacheDir;
private static final String DEST_FILENAME = "dest-filename";
private final String TEST_PAYLOAD1 = "{\"ver\": 1, \"measurements\":" +
"{ \"uptime\": 24982 }, \"data\": {";
private final String TEST_PAYLOAD2 = "\"key1\": \"value1\", \"key2\": \"value2\" } }";
@Override
protected void setUp() throws Exception {
super.setUp();
TelemetryControllerDir = new File(fakeProfileDirectory, "telemetry-ping");
if (!TelemetryControllerDir.mkdir()) {
fail("Could not create directory for telemetry pings.");
}
cacheDir = new File(fakeProfileDirectory, "fakeCacheDir");
if (!cacheDir.mkdir()) {
fail("Could not create directory for fake cacheDir");
}
}
public void testConstructorWithoutParentDir() {
File fileNotDirectory = new File(TelemetryControllerDir, "testFile");
try {
fileNotDirectory.createNewFile();
} catch (IOException e) {
fail("Failed to create new file.");
}
try {
telemetryRecorder = new TelemetryRecorder(fileNotDirectory, cacheDir, "filename");
fail("Should have thrown");
} catch (Exception e) {
Assert.assertTrue(e instanceof IllegalArgumentException);
}
fileNotDirectory.delete();
}
/**
* Check that the real file is not created.
*/
public void testStartPingFile() {
File destFile = new File(DEST_FILENAME);
if (destFile.exists()) {
destFile.delete();
}
telemetryRecorder = new TelemetryRecorder(TelemetryControllerDir, cacheDir, DEST_FILENAME);
try {
telemetryRecorder.startPingFile();
} catch (Exception e) {
fail("Error starting ping file: " + e.getMessage());
}
Assert.assertFalse(destFile.exists());
}
/**
* Check that caller-specified file contains the expected contents and
* verify the checksum.
*/
public void testFinishedPingFile() {
telemetryRecorder = new TelemetryRecorder(TelemetryControllerDir, cacheDir, DEST_FILENAME);
String charset = telemetryRecorder.getCharset();
try {
telemetryRecorder.startPingFile();
telemetryRecorder.appendPayload(TEST_PAYLOAD1);
telemetryRecorder.appendPayload(TEST_PAYLOAD2);
telemetryRecorder.finishPingFile();
} catch (Exception e) {
fail("Error writing payload: " + e);
}
File destFile = new File(TelemetryControllerDir, DEST_FILENAME);
Assert.assertTrue(destFile.exists());
StringBuilder sb = new StringBuilder();
FileInputStream fis = null;
InputStreamReader isr = null;
try {
fis = new FileInputStream(destFile);
isr = new InputStreamReader(fis, charset);;
// Test data is short, and we don't want extra characters in the string.
char[] charBuf = new char[1];
// Read contents into StringBuilder.
while (isr.read(charBuf) != -1) {
sb.append(charBuf);
}
} catch (FileNotFoundException e) {
fail("Unable to find file: " + e);
} catch (UnsupportedEncodingException e) {
fail("Failing with UnsupportedEncodingException: " + e);
} catch (IOException e) {
fail("Failing with IOException: " + e);
} finally {
try {
fis.close();
isr.close();
} catch (IOException e) {
// Do nothing.
}
}
String fileContents = sb.toString();
JSONObject fileJSON = null;
try {
fileJSON = new JSONObject(fileContents);
} catch (JSONException e) {
fail("Failing with bad JSON: " + e);
}
// Check format.
Assert.assertTrue(fileJSON.has("slug"));
Assert.assertTrue(fileJSON.has("payload"));
Assert.assertTrue(fileJSON.has("checksum"));
// Check header.
String pingSlug;
try {
pingSlug = fileJSON.getString("slug");
Assert.assertEquals(DEST_FILENAME, pingSlug);
} catch (JSONException e) {
fail("JSONException attempting to fetch slug: " + e);
}
// Calculate and check the checksum.
try {
MessageDigest checksumDigest = MessageDigest.getInstance("SHA-256");
String payload = fileJSON.getString("payload");
checksumDigest.update(payload.getBytes(charset));
String calculatedChecksum = Base64.encodeToString(checksumDigest.digest(), Base64.NO_WRAP);
String payloadChecksum = fileJSON.getString("checksum");
Assert.assertEquals(payloadChecksum, calculatedChecksum);
} catch (JSONException e) {
fail("Failed to fetch JSON value: " + e);
} catch (NoSuchAlgorithmException e) {
fail("No such MessageDigest algorithm: " + e);
} catch (UnsupportedEncodingException e) {
fail("Unsupported encoding: " + e);
}
}
}

View File

@ -49,7 +49,7 @@ function promiseLoadEvent(browser, url, eventType="load") {
}
// Wait 4 seconds for the pending visits to flush (which should happen in 3 seconds)
const PENDING_VISIT_WAIT = 4000;
const PENDING_VISIT_WAIT = 6000;
// Manage the saved history visits so we can compare in the tests
var gVisitURLs = [];

View File

@ -1184,4 +1184,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1461348254362000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1461412368217000);

View File

@ -1,7 +1,8 @@
0x0a.net: could not connect to host
1password.com: did not receive HSTS header
206rc.net: max-age too low: 2592000
300651.ru: did not receive HSTS header
302.nyc: could not connect to host
302.nyc: did not receive HSTS header
3chit.cf: could not connect to host
4sqsu.eu: could not connect to host
56ct.com: did not receive HSTS header
@ -19,28 +20,27 @@ adsfund.org: could not connect to host
aes256.ru: could not connect to host
aevpn.net: could not connect to host
afp548.tk: could not connect to host
agilebits.net: did not receive HSTS header
agrimap.com: did not receive HSTS header
agrios.de: did not receive HSTS header
ahwatukeefoothillsmontessori.com: could not connect to host
airbnb.com: did not receive HSTS header
aiticon.de: did not receive HSTS header
akclinics.org: did not receive HSTS header
akselimedia.fi: did not receive HSTS header
al-shami.net: did not receive HSTS header
alecvannoten.be: did not receive HSTS header
alenan.org: could not connect to host
allinonecyprus.com: did not receive HSTS header
alpha.irccloud.com: could not connect to host
alphabit-secure.com: could not connect to host
altmv.com: max-age too low: 7776000
amigogeek.net: did not receive HSTS header
andreas-kluge.eu: could not connect to host
andreasbreitenlohner.de: did not receive HSTS header
andreaskluge.eu: could not connect to host
andreasolsson.se: could not connect to host
andymartin.cc: could not connect to host
anfsanchezo.me: could not connect to host
animurecs.com: did not receive HSTS header
ankakaak.com: could not connect to host
anshuman-chatterjee.com: did not receive HSTS header
anycoin.me: could not connect to host
apachelounge.com: did not receive HSTS header
api.mega.co.nz: could not connect to host
@ -59,13 +59,13 @@ asset-alive.net: did not receive HSTS header
atavio.at: could not connect to host
atavio.ch: could not connect to host
atavio.de: did not receive HSTS header
atolm.net: could not connect to host
au.search.yahoo.com: max-age too low: 172800
aurainfosec.com: could not connect to host
auraredeye.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
aurainfosec.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
auraredeye.com: could not connect to host
auraredshield.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
auszeit.bio: did not receive HSTS header
auth.mail.ru: did not receive HSTS header
authentication.io: could not connect to host
auto4trade.nl: could not connect to host
av.de: did not receive HSTS header
avacariu.me: could not connect to host
@ -86,12 +86,12 @@ belairsewvac.com: did not receive HSTS header
benny003.de: did not receive HSTS header
betnet.fr: could not connect to host
bevapehappy.com: did not receive HSTS header
bhatia.at: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
bi.search.yahoo.com: did not receive HSTS header
bidon.ca: did not receive HSTS header
bigdinosaur.org: did not receive HSTS header
bigshinylock.minazo.net: could not connect to host
billninja.com: did not receive HSTS header
binaryevolved.com: could not connect to host
bitfactory.ws: could not connect to host
bitfarm-archiv.com: did not receive HSTS header
bitfarm-archiv.de: did not receive HSTS header
@ -103,14 +103,15 @@ blubbablasen.de: could not connect to host
bluetenmeer.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
bochs.info: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
bodo-wolff.de: did not receive HSTS header
bogosity.se: could not connect to host
bonigo.de: did not receive HSTS header
bonitabrazilian.co.nz: did not receive HSTS header
borchers-media.de: did not receive HSTS header
bowlroll.net: max-age too low: 0
bqtoolbox.com: could not connect to host
braineet.com: did not receive HSTS header
braintreepayments.com: did not receive HSTS header
brainvation.de: did not receive HSTS header
brakstad.org: could not connect to host
brakstad.org: did not receive HSTS header
bran.cc: could not connect to host
branchtrack.com: did not receive HSTS header
brks.xyz: could not connect to host
@ -124,6 +125,7 @@ cabarave.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERR
caesreon.com: did not receive HSTS header
cafe-scientifique.org.ec: could not connect to host
cake.care: could not connect to host
calgaryconstructionjobs.com: did not receive HSTS header
calibreapp.com: did not receive HSTS header
calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
car-navi.ph: did not receive HSTS header
@ -154,16 +156,15 @@ chrome-devtools-frontend.appspot.com: did not receive HSTS header (error ignored
chrome.google.com: did not receive HSTS header (error ignored - included regardless)
chroniclesofgeorge.com: did not receive HSTS header
cidr.ml: did not receive HSTS header
cig-dem.com: could not connect to host
citiagent.cz: did not receive HSTS header
clan-ww.com: could not connect to host
climaprecio.es: did not receive HSTS header
clintonbloodworth.com: could not connect to host
clintonbloodworth.io: could not connect to host
cloudcert.org: did not receive HSTS header
cloudcy.net: did not receive HSTS header
cloudflare.com: did not receive HSTS header
cloudstoragemaus.com: could not connect to host
cloudwalk.io: did not receive HSTS header
clycat.ru: could not connect to host
cmci.dk: did not receive HSTS header
cn.search.yahoo.com: did not receive HSTS header
code.google.com: did not receive HSTS header (error ignored - included regardless)
@ -186,16 +187,15 @@ coursella.com: did not receive HSTS header
cr.search.yahoo.com: did not receive HSTS header
crate.io: did not receive HSTS header
crbug.com: did not receive HSTS header
crow.tw: could not connect to host
crowdcurity.com: did not receive HSTS header
crowdjuris.com: could not connect to host
crypto.is: max-age too low: 7776000
cryptoparty.dk: could not connect to host
crysadm.com: max-age too low: 1
csawctf.poly.edu: could not connect to host
csgokings.eu: could not connect to host
ct.search.yahoo.com: did not receive HSTS header
cujanovic.com: did not receive HSTS header
cvsoftub.com: could not connect to host
cyanogenmod.xxx: could not connect to host
cybershambles.com: could not connect to host
cydia-search.io: could not connect to host
@ -207,7 +207,6 @@ daniel-steuer.de: did not receive HSTS header
darlo.co.uk: could not connect to host
data-abundance.com: could not connect to host
datenkeks.de: did not receive HSTS header
dateno1.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
datsound.ru: did not receive HSTS header
daylightcompany.com: did not receive HSTS header
debtkit.co.uk: did not receive HSTS header
@ -228,10 +227,10 @@ discovery.lookout.com: did not receive HSTS header
dl.google.com: did not receive HSTS header (error ignored - included regardless)
dmxledlights.com: did not receive HSTS header
do.search.yahoo.com: did not receive HSTS header
dobet.in: could not connect to host
docs.google.com: did not receive HSTS header (error ignored - included regardless)
dohosting.ru: could not connect to host
domaris.de: did not receive HSTS header
donmez.uk: could not connect to host
doridian.org: could not connect to host
dotadata.me: could not connect to host
download.jitsi.org: did not receive HSTS header
@ -242,6 +241,7 @@ drive.google.com: did not receive HSTS header (error ignored - included regardle
dropcam.com: did not receive HSTS header
drtroyhendrickson.com: could not connect to host
dymersion.com: did not receive HSTS header
dynamize.solutions: could not connect to host
dzlibs.io: could not connect to host
e-aut.net: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
e-deca2.org: did not receive HSTS header
@ -256,6 +256,7 @@ electromc.com: could not connect to host
elimdengelen.com: did not receive HSTS header
elitefishtank.com: did not receive HSTS header
elnutricionista.es: did not receive HSTS header
elpo.xyz: could not connect to host
elsitar.com: did not receive HSTS header
email.lookout.com: could not connect to host
en-maktoob.search.yahoo.com: did not receive HSTS header
@ -281,7 +282,6 @@ fabianfischer.de: did not receive HSTS header
factorygw.com: did not receive HSTS header
faesser.com: did not receive HSTS header
fant.dk: did not receive HSTS header
fastaim.de: could not connect to host
fatzebra.com.au: did not receive HSTS header
feminists.co: could not connect to host
festember.com: did not receive HSTS header
@ -299,12 +299,14 @@ fixingdns.com: did not receive HSTS header
fj.search.yahoo.com: did not receive HSTS header
fliexer.com: did not receive HSTS header
floweslawncare.com: did not receive HSTS header
fm83.nl: did not receive HSTS header
fonetiq.io: could not connect to host
food4health.guide: could not connect to host
footballmapped.com: could not connect to host
foreignexchangeresource.com: did not receive HSTS header
fotiu.com: could not connect to host
frusky.de: did not receive HSTS header
fredvoyage.fr: could not connect to host
fruchthof24.de: did not receive HSTS header
frusky.net: did not receive HSTS header
futos.de: could not connect to host
g2g.com: did not receive HSTS header
@ -317,17 +319,20 @@ gaptek.id: did not receive HSTS header
geekandi.com: max-age too low: 7776000
geekcast.co.uk: did not receive HSTS header
getable.com: did not receive HSTS header
getlantern.org: could not connect to host
getlantern.org: did not receive HSTS header
gfwsb.ml: could not connect to host
gigacloud.org: could not connect to host
gilly.berlin: did not receive HSTS header
gizzo.sk: could not connect to host
gl.search.yahoo.com: did not receive HSTS header
glass.google.com: did not receive HSTS header (error ignored - included regardless)
globalittech.com: could not connect to host
glws.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
gm-assicurazioni.it: could not connect to host
gm.search.yahoo.com: did not receive HSTS header
gmail.com: did not receive HSTS header (error ignored - included regardless)
gmantra.org: did not receive HSTS header
gmdu.net: max-age too low: 0
goldendata.io: could not connect to host
golfscape.com: max-age too low: 0
goodwin43.ru: did not receive HSTS header
@ -337,7 +342,7 @@ googlemail.com: did not receive HSTS header (error ignored - included regardless
googleplex.com: could not connect to host
googleplex.com: could not connect to host (error ignored - included regardless)
goto.google.com: did not receive HSTS header (error ignored - included regardless)
gotowned.org: did not receive HSTS header
gotowned.org: could not connect to host
gparent.org: did not receive HSTS header
gpsfix.cz: could not connect to host
grandmascookieblog.com: did not receive HSTS header
@ -382,15 +387,16 @@ hookandloom.com: did not receive HSTS header
horosho.in: could not connect to host
horseboners.xxx: did not receive HSTS header
hostedtalkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
hostelinsplit.com: did not receive HSTS header
hostingactive.it: did not receive HSTS header
howrandom.org: could not connect to host
hsts.date: could not connect to host
hstspreload.appspot.com: did not receive HSTS header
hu.search.yahoo.com: did not receive HSTS header
iban.is: could not connect to host
icusignature.com: did not receive HSTS header
id-co.in: could not connect to host
id-conf.com: did not receive HSTS header
ideadozz.hu: could not connect to host
idealsvdr.com: did not receive HSTS header
identitylabs.uk: did not receive HSTS header
ie.search.yahoo.com: did not receive HSTS header
@ -406,11 +412,11 @@ inleaked.com: could not connect to host
inmyarea.com: did not receive HSTS header
innophate-security.nl: could not connect to host
insighti.org: did not receive HSTS header
instacart.com: did not receive HSTS header
intercom.io: did not receive HSTS header
interserved.com: did not receive HSTS header
iop.intuit.com: max-age too low: 86400
iostips.ru: could not connect to host
ip6.li: did not receive HSTS header
ipmimagazine.com: did not receive HSTS header
ipomue.com: could not connect to host
iraqidinar.org: did not receive HSTS header
@ -425,13 +431,15 @@ j0s.at: did not receive HSTS header
jamesdoylephoto.com: did not receive HSTS header
janus-engineering.de: did not receive HSTS header
jayblock.com: did not receive HSTS header
jelmer.co.uk: could not connect to host
jelmer.uk: could not connect to host
jetaprices.com: max-age too low: 0
jhburton.co.uk: could not connect to host
jimmycai.org: max-age too low: 10368000
jinbo123.com: could not connect to host
jkb.pics: could not connect to host
jkbuster.com: could not connect to host
jmdekker.it: could not connect to host
jobss.co.uk: did not receive HSTS header
johners.me: could not connect to host
jonas-keidel.de: did not receive HSTS header
jonathan.ir: could not connect to host
@ -440,6 +448,7 @@ jonathandowning.uk: [Exception... "Component returned failure code: 0x80004005 (
joshstroup.me: max-age too low: 0
jottit.com: could not connect to host
jrvar.com: did not receive HSTS header
juergenhecht.de: did not receive HSTS header
julian-kipka.de: did not receive HSTS header
justlikethat.hosting: did not receive HSTS header
jwilsson.me: could not connect to host
@ -455,6 +464,7 @@ keeleysam.me: could not connect to host
keepclean.me: could not connect to host
keymaster.lookout.com: did not receive HSTS header
kickass.al: could not connect to host
kinderwagen-test24.de: did not receive HSTS header
kingmanhall.org: could not connect to host
kinnon.enterprises: did not receive HSTS header
kirkforcongress.com: could not connect to host
@ -468,7 +478,6 @@ komandakovalchuk.com: [Exception... "Component returned failure code: 0x80004005
koop-bremen.de: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
koordinate.net: did not receive HSTS header
korni22.org: did not receive HSTS header
kotarac.net: did not receive HSTS header
kpvpn.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
kr.search.yahoo.com: did not receive HSTS header
krouzkyliduska.cz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
@ -493,12 +502,12 @@ les-corsaires.net: could not connect to host
letras.mus.br: did not receive HSTS header
li.search.yahoo.com: did not receive HSTS header
library.linode.com: did not receive HSTS header
libraryfreedomproject.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
lifeguard.aecom.com: max-age too low: 86400
liftcannabis.ca: did not receive HSTS header
limalama.eu: max-age too low: 1
linguaquote.com: did not receive HSTS header
litespeed.io: could not connect to host
livedemo.io: could not connect to host
livej.am: could not connect to host
lmintlcx.com: could not connect to host
loftboard.eu: did not receive HSTS header
login.corp.google.com: max-age too low: 7776000 (error ignored - included regardless)
@ -506,9 +515,9 @@ lognot.net: could not connect to host
logotype.se: did not receive HSTS header
lottosonline.com: did not receive HSTS header
lovelycorral.com: did not receive HSTS header
lsky.cn: did not receive HSTS header
lt.search.yahoo.com: did not receive HSTS header
lu.search.yahoo.com: did not receive HSTS header
lucamerega.it: max-age too low: 2592000
lukonet.com: did not receive HSTS header
lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
luxus-russen.de: did not receive HSTS header
@ -521,11 +530,9 @@ mail.google.com: did not receive HSTS header (error ignored - included regardles
makerstuff.net: did not receive HSTS header
malwre.io: could not connect to host
market.android.com: did not receive HSTS header (error ignored - included regardless)
markprof.ru: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
marshut.net: could not connect to host
martijnvhoof.nl: could not connect to host
masa.li: could not connect to host
matrip.de: could not connect to host
mb-is.info: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
mbdb.jp: max-age too low: 0
mcc.re: could not connect to host
@ -541,7 +548,6 @@ megaxchange.com: did not receive HSTS header
meinebo.it: could not connect to host
members.mayfirst.org: did not receive HSTS header
meta-db.com: could not connect to host
miasarafina.de: could not connect to host
miconcinemas.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
micropple.net: could not connect to host
mijn-email.org: could not connect to host
@ -565,6 +571,7 @@ moparisthebest.info: could not connect to host
moriz.net: could not connect to host
movlib.org: could not connect to host
mp3juices.is: could not connect to host
mpintaamalabanna.it: could not connect to host
mqas.net: could not connect to host
msc-seereisen.net: did not receive HSTS header
mt.search.yahoo.com: did not receive HSTS header
@ -572,9 +579,11 @@ mu.search.yahoo.com: did not receive HSTS header
munzee.com: did not receive HSTS header
mustika.cf: max-age too low: 0
mutamatic.com: could not connect to host
mutantmonkey.info: could not connect to host
mw.search.yahoo.com: did not receive HSTS header
my.alfresco.com: did not receive HSTS header
mydigipass.com: did not receive HSTS header
mygov.scot: did not receive HSTS header
mykolab.com: did not receive HSTS header
mykreuzfahrt.de: did not receive HSTS header
myni.io: could not connect to host
@ -605,7 +614,6 @@ nodebrewery.com: could not connect to host
nodetemple.com: did not receive HSTS header
noexpect.org: could not connect to host
nope.website: did not receive HSTS header
notalone.gov: could not connect to host
noworrywp.com: did not receive HSTS header
np.search.yahoo.com: did not receive HSTS header
nu3.at: did not receive HSTS header
@ -613,6 +621,7 @@ nu3.ch: did not receive HSTS header
nu3.co.uk: did not receive HSTS header
nu3.com: did not receive HSTS header
nu3.de: did not receive HSTS header
nu3.dk: did not receive HSTS header
nu3.fr: did not receive HSTS header
nu3.se: did not receive HSTS header
nullpoint.at: did not receive HSTS header
@ -621,6 +630,8 @@ nutsandboltsmedia.com: did not receive HSTS header
nystart.no: did not receive HSTS header
nz.search.yahoo.com: max-age too low: 172800
nzb.cat: did not receive HSTS header
o7.com: max-age too low: 0
odin.xxx: could not connect to host
onet.space: could not connect to host
ooonja.de: could not connect to host
open-to-repair.fr: did not receive HSTS header
@ -629,6 +640,7 @@ openshift.redhat.com: did not receive HSTS header
orhideous.name: could not connect to host
otakuworld.de: did not receive HSTS header
ottospora.nl: could not connect to host
otya.me: could not connect to host
ourbank.com: max-age too low: 604800
ouvirmusica.com.br: did not receive HSTS header
override.io: did not receive HSTS header
@ -649,16 +661,17 @@ patterson.mp: did not receive HSTS header
pauladamsmith.com: did not receive HSTS header
pbprint.ru: max-age too low: 0
peissen.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
pepchid.com: could not connect to host
perfectionis.me: could not connect to host
personaldatabasen.no: could not connect to host
petersmark.com: did not receive HSTS header
petrolplus.ru: did not receive HSTS header
pfd-nz.com: did not receive HSTS header
phil.tw: could not connect to host
phongmay24h.com: could not connect to host
phurl.de: could not connect to host
pic.gov: did not receive HSTS header
picksin.club: could not connect to host
pierre-denoblens.net: could not connect to host
pinesandneedles.com: did not receive HSTS header
piratenlogin.de: could not connect to host
pirati.cz: max-age too low: 604800
@ -673,30 +686,30 @@ plothost.com: did not receive HSTS header
poiema.com.sg: did not receive HSTS header
pointiswunderland.de: did not receive HSTS header
pol.in.th: could not connect to host
polymathematician.com: could not connect to host
polypho.nyc: could not connect to host
popcorntime.ws: did not receive HSTS header
posterspy.com: did not receive HSTS header
powercloud.technology: did not receive HSTS header
pr.search.yahoo.com: did not receive HSTS header
prefontaine.name: could not connect to host
preissler.co.uk: could not connect to host
pressfreedomfoundation.org: did not receive HSTS header
privacyrup.net: could not connect to host
prodpad.com: did not receive HSTS header
production.vn: did not receive HSTS header
profi-durchgangsmelder.de: did not receive HSTS header
promecon-gmbh.de: did not receive HSTS header
prontolight.com: did not receive HSTS header
proximato.com: could not connect to host
proxybay.club: did not receive HSTS header
proxybay.info: did not receive HSTS header
pult.co: could not connect to host
punchr-kamikazee.rhcloud.com: did not receive HSTS header
pushapp.org: could not connect to host
py.search.yahoo.com: did not receive HSTS header
pyplo.org: did not receive HSTS header
qingxuan.info: did not receive HSTS header
quantumcourse.org: did not receive HSTS header
quotehex.com: did not receive HSTS header
ragingserenity.com: could not connect to host
raiseyourflag.com: did not receive HSTS header
rapidresearch.me: could not connect to host
rasing.me: could not connect to host
@ -731,8 +744,6 @@ sah3.net: could not connect to host
sakaki.anime.my: max-age too low: 5184000
salserocafe.com: did not receive HSTS header
salserototal.com: did not receive HSTS header
sambeso.net: could not connect to host
sarahlicity.co.uk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
saturngames.co.uk: could not connect to host
savetheinternet.eu: did not receive HSTS header
scanpay.dk: did not receive HSTS header
@ -763,16 +774,18 @@ shellsec.pw: did not receive HSTS header
shiinko.com: could not connect to host
shoprose.ru: did not receive HSTS header
shops.neonisi.com: could not connect to host
shukatsu-note.com: did not receive HSTS header
siammedia.co: did not receive HSTS header
sifls.com: could not connect to host
silentcircle.org: could not connect to host
silkebaekken.no: could not connect to host
simon.butcher.name: max-age too low: 2629743
simplelearner.com: could not connect to host
simplyfixit.co.uk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
siriad.com: did not receive HSTS header
sistemy48.ru: did not receive HSTS header
sites.google.com: did not receive HSTS header (error ignored - included regardless)
skogsbruket.fi: could not connect to host
skogskultur.fi: could not connect to host
slamix.nl: did not receive HSTS header
slicketl.com: did not receive HSTS header
slix.io: could not connect to host
@ -796,11 +809,11 @@ spdysync.com: did not receive HSTS header
spencerbaer.com: did not receive HSTS header
spreadsheets.google.com: did not receive HSTS header (error ignored - included regardless)
sqzryang.com: max-age too low: 604800
srevilak.net: did not receive HSTS header
ssl.google-analytics.com: did not receive HSTS header (error ignored - included regardless)
ssl.panoramio.com: did not receive HSTS header
stassi.ch: did not receive HSTS header
stassi.ch: could not connect to host
staticanime.net: did not receive HSTS header
stationary-traveller.eu: could not connect to host
stick2bike.de: did not receive HSTS header
stillyarts.com: did not receive HSTS header
stocktrade.de: could not connect to host
@ -835,22 +848,21 @@ tcl.ath.cx: did not receive HSTS header
techelements.co: did not receive HSTS header
techhub.ml: could not connect to host
techloaner.com: could not connect to host
technotonic.com.au: did not receive HSTS header
tegelsensanitaironline.nl: did not receive HSTS header
tekshrek.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
tektoria.de: did not receive HSTS header
tektoria.de: could not connect to host
temehu.com: did not receive HSTS header
terrax.berlin: could not connect to host
terrax.info: could not connect to host
terrax.net: could not connect to host
terrty.net: could not connect to host
the-sky-of-valkyries.com: could not connect to host
theater.cf: could not connect to host
thebrotherswarde.com: could not connect to host
thecoffeehouse.xyz: could not connect to host
thehiddenbay.net: could not connect to host
thehistory.me: did not receive HSTS header
thepartywarehouse.co.uk: did not receive HSTS header
therapyportal.com: did not receive HSTS header
thetomharling.com: did not receive HSTS header
thinkindifferent.net: could not connect to host
thorncreek.net: did not receive HSTS header
thumbtack.com: did not receive HSTS header
@ -862,7 +874,6 @@ tirex.media: did not receive HSTS header
titties.ml: could not connect to host
tlo.network: did not receive HSTS header
tm-solutions.eu: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
tobias-kluge.de: could not connect to host
tollmanz.com: did not receive HSTS header
tomfisher.eu: could not connect to host
tomrichards.net: did not receive HSTS header
@ -887,7 +898,7 @@ tv.search.yahoo.com: could not connect to host
tvtubeflix.com: did not receive HSTS header
twist.party: could not connect to host
ua.search.yahoo.com: did not receive HSTS header
ubicv.com: max-age too low: 0
uberfunction.com: did not receive HSTS header
uega.net: did not receive HSTS header
ukas.com: did not receive HSTS header
unapp.me: could not connect to host
@ -895,7 +906,7 @@ unbanthe.net: did not receive HSTS header
univz.com: could not connect to host
unun.fi: could not connect to host
uonstaffhub.com: could not connect to host
upay.ru: did not receive HSTS header
upay.ru: could not connect to host
uprotect.it: could not connect to host
upstats.eu: could not connect to host
ustr.gov: max-age too low: 86400
@ -911,6 +922,7 @@ vhost.co.id: could not connect to host
videnskabsklubben.dk: did not receive HSTS header
viennan.net: could not connect to host
vmrdev.com: could not connect to host
voliere-info.nl: did not receive HSTS header
vortexhobbies.com: did not receive HSTS header
votocek.cz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
votockova.cz: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
@ -924,17 +936,16 @@ webassadors.com: could not connect to host
webeau.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
webmail.mayfirst.org: did not receive HSTS header
webmaniabr.com: did not receive HSTS header
webstudio-n.com: could not connect to host
webswitch.io: could not connect to host
werdeeintimo.de: did not receive HSTS header
wesecom.com: did not receive HSTS header
wevahoo.com: could not connect to host
when-release.com: did not receive HSTS header
whitestagforge.com: did not receive HSTS header
wholebites.com: did not receive HSTS header
whyworldhot.com: did not receive HSTS header
wikidsystems.com: did not receive HSTS header
wilf1rst.com: could not connect to host
winclient.cn: could not connect to host
winclient.cn: did not receive HSTS header
withgoogle.com: did not receive HSTS header (error ignored - included regardless)
withustrading.com: could not connect to host
withyoutube.com: did not receive HSTS header (error ignored - included regardless)
@ -942,7 +953,7 @@ wiz.biz: could not connect to host
wohnungsbau-ludwigsburg.de: did not receive HSTS header
woima.fi: max-age too low: 604800
wover.me: did not receive HSTS header
writeapp.me: did not receive HSTS header
ww2onlineshop.com: did not receive HSTS header
www.apollo-auto.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
www.calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
www.cueup.com: could not connect to host
@ -963,7 +974,10 @@ www.surfeasy.com: did not receive HSTS header
xa.search.yahoo.com: did not receive HSTS header
xavierbarroso.com: did not receive HSTS header
xcoop.me: could not connect to host
xellos.ga: could not connect to host
xellos.ml: could not connect to host
xenesisziarovky.sk: could not connect to host
xf-liam.com: did not receive HSTS header
xiaody.me: could not connect to host
xiaoxiao.im: could not connect to host
xplore-dna.net: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
@ -974,6 +988,7 @@ xtreamhosting.eu: could not connect to host
xtremegaming.it: did not receive HSTS header
y-o-w.com: did not receive HSTS header
yaporn.tv: max-age too low: 0
yenniferallulli.es: could not connect to host
yenniferallulli.moda: could not connect to host
yetii.net: did not receive HSTS header
yingyj.com: did not receive HSTS header
@ -992,6 +1007,6 @@ zentraler-kreditausschuss.de: max-age too low: 0
zh.search.yahoo.com: did not receive HSTS header
zirtue.io: could not connect to host
zixiao.wang: could not connect to host
zking.ga: could not connect to host
zking.ga: did not receive HSTS header
zoo24.de: did not receive HSTS header
zzsec.org: did not receive HSTS header

File diff suppressed because it is too large Load Diff

View File

@ -65,10 +65,14 @@ function runTest() {
false, "foobarcookie", observer, alertName);
ok(true, "showAlertNotification() succeeded. Waiting for notification...");
if (SpecialPowers.Services.appinfo.OS == "Darwin") {
// Notifications are native on OS X 10.8 and later, and when they are they
// persist in the Notification Center. We need to close explicitly to avoid a hang.
// This also works for XUL notifications when running this test on OS X < 10.8.
if ("@mozilla.org/system-alerts-service;1" in Cc) {
// Notifications are native on OS X 10.8 and later, as well as GNOME
// Shell with libnotify (bug 1236036). These notifications persist in the
// Notification Center, and only fire the `alertfinished` event when
// closed. For platforms where native notifications may be used, we need
// to close explicitly to avoid a hang. This also works for XUL
// notifications when running this test on OS X < 10.8, or a window
// manager like Ubuntu Unity with incomplete libnotify support.
notifier.closeAlert(alertName);
}
} catch (ex) {

View File

@ -37,7 +37,6 @@ BackgroundPage.prototype = {
}
let uri = Services.io.newURI(url, null, null);
let principal = this.extension.createPrincipal(uri);
let system = Services.scriptSecurityManager.getSystemPrincipal();
let interfaceRequestor = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor);
@ -57,8 +56,6 @@ BackgroundPage.prototype = {
this.context = new ExtensionPage(this.extension, {type: "background", docShell, uri});
GlobalManager.injectInDocShell(docShell, this.extension, this.context);
docShell.createAboutBlankContentViewer(principal);
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
this.webNav = webNav;
@ -107,6 +104,7 @@ BackgroundPage.prototype = {
this.webNav = null;
this.chromeWebNav.loadURI("about:blank", 0, null, null, null);
this.chromeWebNav.close();
this.chromeWebNav = null;
},
};

View File

@ -1039,6 +1039,7 @@ EnvironmentCache.prototype = {
if (AppConstants.platform === "gonk") {
return true;
}
if (!("@mozilla.org/browser/shell-service;1" in Cc)) {
this._log.info("_isDefaultBrowser - Could not obtain browser shell service");
return null;
@ -1046,11 +1047,21 @@ EnvironmentCache.prototype = {
let shellService;
try {
shellService = Cc["@mozilla.org/browser/shell-service;1"]
.getService(Ci.nsIShellService);
let scope = {};
Cu.import("resource:///modules/ShellService.jsm", scope);
shellService = scope.ShellService;
} catch (ex) {
this._log.error("_isDefaultBrowser - Could not obtain shell service", ex);
return null;
this._log.error("_isDefaultBrowser - Could not obtain shell service JSM");
}
if (!shellService) {
try {
shellService = Cc["@mozilla.org/browser/shell-service;1"]
.getService(Ci.nsIShellService);
} catch (ex) {
this._log.error("_isDefaultBrowser - Could not obtain shell service", ex);
return null;
}
}
try {

View File

@ -1,4 +1,3 @@
#filter substitution
/* 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/. */

View File

@ -14,6 +14,7 @@ Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", this);
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/UpdateUtils.jsm", this);
const PREF_BRANCH = "toolkit.telemetry.";
const PREF_SERVER = PREF_BRANCH + "server";
@ -40,6 +41,21 @@ function fakeResetAcceptedPolicy() {
Preferences.reset(PREF_ACCEPTED_POLICY_VERSION);
}
function setMinimumPolicyVersion(aNewPolicyVersion) {
const CHANNEL_NAME = UpdateUtils.getUpdateChannel(false);
// We might have channel-dependent minimum policy versions.
const CHANNEL_DEPENDENT_PREF = PREF_MINIMUM_POLICY_VERSION + ".channel-" + CHANNEL_NAME;
// Does the channel-dependent pref exist? If so, set its value.
if (Preferences.get(CHANNEL_DEPENDENT_PREF, undefined)) {
Preferences.set(CHANNEL_DEPENDENT_PREF, aNewPolicyVersion);
return;
}
// We don't have a channel specific minimu, so set the common one.
Preferences.set(PREF_MINIMUM_POLICY_VERSION, aNewPolicyVersion);
}
function run_test() {
// Addon manager needs a profile directory
do_get_profile(true);
@ -109,7 +125,7 @@ add_task(function* test_prefs() {
// Set a new minimum policy version and check that user is no longer notified.
let newMinimum = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) + 1;
Preferences.set(PREF_MINIMUM_POLICY_VERSION, newMinimum);
setMinimumPolicyVersion(newMinimum);
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
"A greater minimum policy version must invalidate the policy and disable upload.");

View File

@ -9,6 +9,7 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
XPIDL_SOURCES += [
'nsIAppShellService.idl',
'nsIPopupWindowManager.idl',
'nsIWindowlessBrowser.idl',
'nsIWindowMediator.idl',
'nsIWindowMediatorListener.idl',
'nsIXULBrowserWindow.idl',

View File

@ -34,10 +34,13 @@
#include "nsIScriptContext.h"
#include "nsAppShellService.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "nsISupportsPrimitives.h"
#include "nsIChromeRegistry.h"
#include "nsILoadContext.h"
#include "nsIWebNavigation.h"
#include "nsIWindowlessBrowser.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
@ -399,43 +402,100 @@ WebBrowserChrome2Stub::Blur()
return NS_ERROR_NOT_IMPLEMENTED;
}
// This is the "stub" we return from CreateWindowlessBrowser - it exists
// purely to keep a strong reference to the browser and the container to
// prevent the container being collected while the stub remains alive.
class WindowlessBrowserStub final : public nsIWebNavigation,
public nsIInterfaceRequestor
class BrowserDestroyer final : public nsRunnable
{
public:
WindowlessBrowserStub(nsIWebBrowser *aBrowser, nsISupports *aContainer) {
mBrowser = aBrowser;
BrowserDestroyer(nsIWebBrowser *aBrowser, nsISupports *aContainer) :
mBrowser(aBrowser),
mContainer(aContainer)
{
}
NS_IMETHOD
Run() override
{
// Explicitly destroy the browser, in case this isn't the last reference.
nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
return window->Destroy();
}
protected:
virtual ~BrowserDestroyer() {}
private:
nsCOMPtr<nsIWebBrowser> mBrowser;
nsCOMPtr<nsISupports> mContainer;
};
// This is the "stub" we return from CreateWindowlessBrowser - it exists
// to manage the lifetimes of the nsIWebBrowser and container window.
// In particular, it keeps a strong reference to both, to prevent them from
// being collected while this object remains alive, and ensures that they
// aren't destroyed when it's not safe to run scripts.
class WindowlessBrowser final : public nsIWindowlessBrowser,
public nsIInterfaceRequestor
{
public:
WindowlessBrowser(nsIWebBrowser *aBrowser, nsISupports *aContainer) :
mBrowser(aBrowser),
mContainer(aContainer),
mClosed(false)
{
mWebNavigation = do_QueryInterface(aBrowser);
mInterfaceRequestor = do_QueryInterface(aBrowser);
mContainer = aContainer;
}
NS_DECL_ISUPPORTS
NS_FORWARD_NSIWEBNAVIGATION(mWebNavigation->)
NS_FORWARD_NSIINTERFACEREQUESTOR(mInterfaceRequestor->)
NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)
NS_IMETHOD
Close() override
{
NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"WindowlessBrowser::Close called when not safe to run scripts");
mClosed = true;
mWebNavigation = nullptr;
mInterfaceRequestor = nullptr;
nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
return window->Destroy();
}
protected:
virtual ~WindowlessBrowser()
{
if (mClosed) {
return;
}
NS_WARNING("Windowless browser was not closed prior to destruction");
// The docshell destructor needs to dispatch events, and can only run
// when it's safe to run scripts. If this was triggered by GC, it may
// not always be safe to run scripts, in which cases we need to delay
// destruction until it is.
nsCOMPtr<nsIRunnable> runnable = new BrowserDestroyer(mBrowser, mContainer);
nsContentUtils::AddScriptRunner(runnable);
}
private:
~WindowlessBrowserStub() {}
nsCOMPtr<nsIWebBrowser> mBrowser;
nsCOMPtr<nsIWebNavigation> mWebNavigation;
nsCOMPtr<nsIInterfaceRequestor> mInterfaceRequestor;
// we don't use the container but just hold a reference to it.
nsCOMPtr<nsISupports> mContainer;
bool mClosed;
};
NS_INTERFACE_MAP_BEGIN(WindowlessBrowserStub)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebNavigation)
NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(WindowlessBrowserStub)
NS_IMPL_RELEASE(WindowlessBrowserStub)
NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation, nsIInterfaceRequestor)
NS_IMETHODIMP
nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWebNavigation **aResult)
nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult)
{
/* First, we create an instance of nsWebBrowser. Instances of this class have
* an associated doc shell, which is what we're interested in.
@ -480,7 +540,7 @@ nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWebNavigation **aR
window->Create();
nsISupports *isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome2*, stub);
RefPtr<nsIWebNavigation> result = new WindowlessBrowserStub(browser, isstub);
RefPtr<nsIWindowlessBrowser> result = new WindowlessBrowser(browser, isstub);
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result);
docshell->SetInvisible(true);

View File

@ -6,7 +6,7 @@
#include "nsISupports.idl"
interface nsIXULWindow;
interface nsIWebNavigation;
interface nsIWindowlessBrowser;
interface nsIURI;
interface nsIDOMWindow;
interface nsIAppShell;
@ -18,7 +18,7 @@ interface nsITabParent;
#include "js/TypeDecls.h"
%}
[scriptable, uuid(83f23c7e-6ce0-433f-9fe2-f287ae8c6e0c)]
[scriptable, uuid(0cc1c790-6873-4b31-944f-71d3725e373d)]
interface nsIAppShellService : nsISupports
{
/**
@ -53,7 +53,7 @@ interface nsIAppShellService : nsISupports
* representation.
* @param aIsChrome Set true if you want to use it for chrome content.
*/
nsIWebNavigation createWindowlessBrowser([optional] in bool aIsChrome);
nsIWindowlessBrowser createWindowlessBrowser([optional] in bool aIsChrome);
[noscript]
void createHiddenWindow();

View File

@ -0,0 +1,27 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 "nsIWebNavigation.idl"
/**
* This interface represents a nsIWebBrowser instance with no associated OS
* window. Its main function is to manage the lifetimes of those windows.
* A strong reference to this object must be held until the window is
* ready to be destroyed.
*/
[scriptable, uuid(abb46f48-abfc-41bf-aa9a-7feccefcf977)]
interface nsIWindowlessBrowser : nsIWebNavigation
{
/**
* "Closes" the windowless browser and destroys its associated nsIWebBrowser
* and docshell.
*
* This method *must* be called for every windowless browser before its last
* reference is released.
*/
void close();
};

View File

@ -59,6 +59,8 @@ function testWindowlessBrowser(chromePrivileged) {
}
ok(!exception, "window.external.AddSearchProvider should be ignore withour raising an exception");
webNav.close();
}
info("Test Bug 1214174 on a content privileged windowless browser");