mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 21:35:39 +00:00
Merge m-c to inbound. a=merge
This commit is contained in:
commit
d8ad3e5215
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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. -->
|
||||
|
@ -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. -->
|
||||
|
@ -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. -->
|
||||
|
@ -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"/>
|
||||
|
@ -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. -->
|
||||
|
@ -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. -->
|
||||
|
@ -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. -->
|
||||
|
@ -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"/>
|
||||
|
@ -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. -->
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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. -->
|
||||
|
@ -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. -->
|
||||
|
@ -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"/>
|
||||
|
@ -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">
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -329,6 +329,7 @@ const PanelUI = {
|
||||
evt.initCustomEvent("ViewShowing", true, true, viewNode);
|
||||
viewNode.dispatchEvent(evt);
|
||||
if (evt.defaultPrevented) {
|
||||
aAnchor.open = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -10,6 +10,9 @@
|
||||
"XPCOMUtils": true,
|
||||
"Task": true,
|
||||
|
||||
// Browser window globals.
|
||||
"PanelUI": false,
|
||||
|
||||
// Test harness globals
|
||||
"ExtensionTestUtils": false,
|
||||
|
||||
|
@ -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]);
|
||||
});
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -63,6 +63,7 @@ add_task(function* () {
|
||||
|
||||
clickBrowserAction(extension);
|
||||
yield extension.awaitMessage("popup-finished");
|
||||
yield closeBrowserAction(extension);
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
@ -172,3 +172,5 @@ add_task(function* test_url() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(forceGC);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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",
|
||||
|
86
browser/components/shell/ShellService.jsm
Normal file
86
browser/components/shell/ShellService.jsm
Normal 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];
|
||||
}
|
||||
});
|
@ -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]
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
19
browser/components/shell/nsIGNOMEShellService.idl
Normal file
19
browser/components/shell/nsIGNOMEShellService.idl
Normal 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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "nsIShellService.idl"
|
||||
|
||||
[scriptable, uuid(13f20725-4fd5-431f-90a1-525ab31755b1)]
|
||||
[scriptable, uuid(f8a26b94-49e5-4441-8fbc-315e0b4f22ef)]
|
||||
interface nsIWindowsShellService : nsIShellService
|
||||
{
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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____
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,6 @@ protected:
|
||||
nsresult LaunchModernSettingsDialogDefaultApps();
|
||||
nsresult InvokeHTTPOpenAsVerb();
|
||||
nsresult LaunchHTTPHandlerPane();
|
||||
|
||||
private:
|
||||
bool mCheckedThisSession;
|
||||
};
|
||||
|
||||
#endif // nswindowsshellservice_h____
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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. -->
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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',
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 = [];
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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) {
|
||||
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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/. */
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -9,6 +9,7 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
XPIDL_SOURCES += [
|
||||
'nsIAppShellService.idl',
|
||||
'nsIPopupWindowManager.idl',
|
||||
'nsIWindowlessBrowser.idl',
|
||||
'nsIWindowMediator.idl',
|
||||
'nsIWindowMediatorListener.idl',
|
||||
'nsIXULBrowserWindow.idl',
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
27
xpfe/appshell/nsIWindowlessBrowser.idl
Normal file
27
xpfe/appshell/nsIWindowlessBrowser.idl
Normal 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();
|
||||
};
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user