Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-02-25 11:59:05 +01:00
commit e232fcd2d4
88 changed files with 1156 additions and 276 deletions

View File

@ -1291,9 +1291,6 @@ var gBrowserInit = {
gBrowserThumbnails.init();
// Add Devtools menuitems and listeners
gDevToolsBrowser.registerBrowserWindow(window);
gMenuButtonBadgeManager.init();
gMenuButtonUpdateBadge.init();
@ -1407,8 +1404,6 @@ var gBrowserInit = {
if (!this._loadHandled)
return;
gDevToolsBrowser.forgetBrowserWindow(window);
let desc = Object.getOwnPropertyDescriptor(window, "DeveloperToolbar");
if (desc && !desc.get) {
DeveloperToolbar.destroy();
@ -2702,8 +2697,7 @@ var BrowserOnClick = {
break;
case "Browser:SendSSLErrorReport":
this.onSSLErrorReport(msg.target,
msg.data.documentURI,
msg.data.location,
msg.data.uri,
msg.data.securityInfo);
break;
case "Browser:SetSSLErrorReportAuto":
@ -2723,7 +2717,7 @@ var BrowserOnClick = {
let weakCryptoOverride = Cc["@mozilla.org/security/weakcryptooverride;1"]
.getService(Ci.nsIWeakCryptoOverride);
weakCryptoOverride.addWeakCryptoOverride(
msg.data.location.hostname,
msg.data.uri.host,
PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser));
break;
case "Browser:SSLErrorGoBack":
@ -2732,7 +2726,7 @@ var BrowserOnClick = {
}
},
onSSLErrorReport: function(browser, documentURI, location, securityInfo) {
onSSLErrorReport: function(browser, uri, securityInfo) {
if (!Services.prefs.getBoolPref("security.ssl.errorReporting.enabled")) {
Cu.reportError("User requested certificate error report sending, but certificate error reporting is disabled");
return;
@ -2745,11 +2739,8 @@ var BrowserOnClick = {
let errorReporter = Cc["@mozilla.org/securityreporter;1"]
.getService(Ci.nsISecurityReporter);
// if location.port is the empty string, set to -1 (for consistency with
// port values from nsIURI)
let port = location.port === "" ? -1 : location.port;
errorReporter.reportTLSError(transportSecurityInfo,
location.hostname, port);
uri.host, uri.port);
},
onAboutCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) {
@ -6543,6 +6534,10 @@ var gIdentityHandler = {
return this._state & Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
},
get _isCertUserOverridden() {
return this._state & Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN;
},
get _hasInsecureLoginForms() {
// checks if the page has been flagged for an insecure login. Also checks
// if the pref to degrade the UI is set to true
@ -6832,23 +6827,14 @@ var gIdentityHandler = {
if (this._isMixedActiveContentBlocked) {
this._identityBox.classList.add("mixedActiveBlocked");
}
let iData = this.getIdentityData();
// Verifier is either the CA Org, for a normal cert, or a special string
// for certs that are trusted because of a security exception.
tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
[iData.caOrg]);
let host = this._uri.host;
let port = 443;
try {
if (this._uri.port > 0)
port = this._uri.port;
} catch (e) {}
if (this._overrideService.hasMatchingOverride(host, port, iData.cert, {}, {})) {
if (this._isCertUserOverridden) {
this._identityBox.classList.add("certUserOverridden");
// Cert is trusted because of a security exception, verifier is a special string.
tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
} else {
// It's a normal cert, verifier is the CA Org.
tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
[this.getIdentityData().caOrg]);
}
} else {
this._identityBox.className = "unknownIdentity";
@ -6952,6 +6938,8 @@ var gIdentityHandler = {
connection = "file";
} else if (this._isEV) {
connection = "secure-ev";
} else if (this._isCertUserOverridden) {
connection = "secure-cert-user-overridden";
} else if (this._isSecure) {
connection = "secure";
}

View File

@ -99,7 +99,8 @@ var handleContentContextMenu = function (event) {
Services.obs.notifyObservers(subject, "content-contextmenu", null);
let doc = event.target.ownerDocument;
let docLocation = doc.location ? doc.location.href : undefined;
let docLocation = doc.mozDocumentURIIfNotForErrorPages;
docLocation = docLocation && docLocation.spec;
let charSet = doc.characterSet;
let baseURI = doc.baseURI;
let referrer = doc.referrer;
@ -276,9 +277,6 @@ var AboutCertErrorListener = {
// if we're enabling reports, send a report for this failure
if (event.detail) {
let doc = content.document;
let location = doc.location.href;
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
@ -288,9 +286,9 @@ var AboutCertErrorListener = {
let serializedSecurityInfo = serhelper.serializeToString(serializable);
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
sendAsyncMessage("Browser:SendSSLErrorReport", {
documentURI: doc.documentURI,
location: {hostname: doc.location.hostname, port: doc.location.port},
uri: { host, port },
securityInfo: serializedSecurityInfo
});
}
@ -349,10 +347,6 @@ var AboutNetErrorListener = {
// if we're enabling reports, send a report for this failure
if (evt.detail) {
let contentDoc = content.document;
let location = contentDoc.location.href;
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
@ -362,12 +356,9 @@ var AboutNetErrorListener = {
let serializedSecurityInfo = serhelper.serializeToString(serializable);
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
sendAsyncMessage("Browser:SendSSLErrorReport", {
documentURI: contentDoc.documentURI,
location: {
hostname: contentDoc.location.hostname,
port: contentDoc.location.port
},
uri: { host, port },
securityInfo: serializedSecurityInfo
});
@ -375,13 +366,8 @@ var AboutNetErrorListener = {
},
onOverride: function(evt) {
let contentDoc = content.document;
let location = contentDoc.location;
sendAsyncMessage("Browser:OverrideWeakCrypto", {
documentURI: contentDoc.documentURI,
location: {hostname: location.hostname, port: location.port}
});
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
sendAsyncMessage("Browser:OverrideWeakCrypto", { uri: {host, port} });
}
}

View File

@ -688,8 +688,9 @@ Sanitizer.PREF_SANITIZE_ON_SHUTDOWN = "privacy.sanitize.sanitizeOnShutdown";
// by a crash.
Sanitizer.PREF_SANITIZE_IN_PROGRESS = "privacy.sanitize.sanitizeInProgress";
// Whether the previous shutdown sanitization completed successfully.
// Note that PREF_SANITIZE_IN_PROGRESS would be enough to detect an interrupted
// sanitization, but this is still supported for backwards compatibility.
// This is used to detect cases where we were supposed to sanitize on shutdown
// but due to a crash we were unable to. In such cases there may not be any
// sanitization in progress, cause we didn't have a chance to start it yet.
Sanitizer.PREF_SANITIZE_DID_SHUTDOWN = "privacy.sanitize.didShutdownSanitize";
// Time span constants corresponding to values of the privacy.sanitize.timeSpan
@ -779,10 +780,14 @@ Sanitizer.onStartup = Task.async(function*() {
let shutownSanitizationWasInterrupted =
Preferences.get(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, false) &&
!Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN);
// Regardless, reset the pref, since we want to check it at the next startup
// even if the browser exits abruptly.
Preferences.reset(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN);
Services.prefs.savePrefFile(null);
if (Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN)) {
// Reset the pref, so that if we crash before having a chance to
// sanitize on shutdown, we will do at the next startup.
// Flushing prefs has a cost, so do this only if necessary.
Preferences.reset(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN);
Services.prefs.savePrefFile(null);
}
// Make sure that we are triggered during shutdown, at the right time,
// and only once.
@ -804,7 +809,7 @@ Sanitizer.onStartup = Task.async(function*() {
}
placesClient.addBlocker("sanitize.js: Sanitize on shutdown", doSanitize);
// Check if Firefox crashed before completing a sanitization.
// Check if Firefox crashed during a sanitization.
let lastInterruptedSanitization = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS, "");
if (lastInterruptedSanitization) {
let s = new Sanitizer();
@ -812,11 +817,9 @@ Sanitizer.onStartup = Task.async(function*() {
let itemsToClear = JSON.parse(lastInterruptedSanitization);
yield s.sanitize(itemsToClear);
} else if (shutownSanitizationWasInterrupted) {
// Ideally lastInterruptedSanitization should always be set when a
// sanitization is interrupted, but some add-ons or Firefox previous
// versions may not set the pref.
// In such a case, we can still detect an interrupted shutdown sanitization,
// and just redo it.
// Otherwise, could be we were supposed to sanitize on shutdown but we
// didn't have a chance, due to an earlier crash.
// In such a case, just redo a shutdown sanitize now, during startup.
yield Sanitizer.onShutdown();
}
});

View File

@ -64,6 +64,11 @@ SocialErrorListener = {
},
receiveMessage(message) {
if (!content) {
Cu.reportError("Message received whilst `content` is null: " + message.name);
return;
}
let document = content.document;
switch (message.name) {

View File

@ -4,6 +4,8 @@
Components.utils.import("resource://gre/modules/Promise.jsm", this);
requestLongerTimeout(2);
var chatbar = document.getElementById("pinnedchats");
add_chat_task(function* testOpenCloseChat() {

View File

@ -7,6 +7,8 @@
Components.utils.import("resource://gre/modules/Promise.jsm", this);
const CHAT_URL = "https://example.com/browser/browser/base/content/test/chat/chat.html";
requestLongerTimeout(2);
// Is the currently opened tab focused?
function isTabFocused() {
let tabb = gBrowser.getBrowserForTab(gBrowser.selectedTab);

View File

@ -155,6 +155,7 @@ skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
[browser_beforeunload_duplicate_dialogs.js]
[browser_blob-channelname.js]
[browser_bookmark_popup.js]
skip-if = (os == "linux" && debug) # mouseover not reliable on linux debug builds
[browser_bookmark_titles.js]
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
[browser_bug304198.js]

View File

@ -8,7 +8,7 @@
// a bad certificate, being redirected to the internal about:certerror page,
// using the button contained therein to load the certificate exception
// dialog, using that to add an exception, and finally successfully visiting
// the site.
// the site, including showing the right identity box and control center icons.
function test() {
waitForExplicitFinish();
whenNewTabLoaded(window, loadBadCertPage);
@ -67,6 +67,7 @@ var certExceptionDialogObserver = {
var successfulLoadListener = {
handleEvent: function() {
gBrowser.selectedBrowser.removeEventListener("load", this, true);
checkControlPanelIcons();
let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
.getService(Ci.nsICertOverrideService);
certOverrideService.clearValidityOverride("expired.example.com", -1);
@ -75,6 +76,35 @@ var successfulLoadListener = {
}
};
// Check for the correct icons in the identity box and control center.
function checkControlPanelIcons() {
let { gIdentityHandler } = gBrowser.ownerGlobal;
gIdentityHandler._identityBox.click();
document.getElementById("identity-popup-security-expander").click();
is_element_visible(document.getElementById("connection-icon"));
let connectionIconImage = gBrowser.ownerGlobal
.getComputedStyle(document.getElementById("connection-icon"), "")
.getPropertyValue("list-style-image");
let securityViewBG = gBrowser.ownerGlobal
.getComputedStyle(document.getElementById("identity-popup-securityView"), "")
.getPropertyValue("background-image");
let securityContentBG = gBrowser.ownerGlobal
.getComputedStyle(document.getElementById("identity-popup-security-content"), "")
.getPropertyValue("background-image");
is(connectionIconImage,
"url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
"Using expected icon image in the identity block");
is(securityViewBG,
"url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
"Using expected icon image in the Control Center main view");
is(securityContentBG,
"url(\"chrome://browser/skin/identity-mixed-passive-loaded.svg\")",
"Using expected icon image in the Control Center subview");
gIdentityHandler._identityPopup.hidden = true;
}
// Utility function to get a handle on the certificate exception dialog.
// Modified from toolkit/components/passwordmgr/test/prompt_common.js
function getDialog(aLocation) {

View File

@ -25,7 +25,7 @@
<label observes="identity-popup-content-host"/>
<description class="identity-popup-connection-not-secure"
value="&identity.connectionNotSecure;"
when-connection="not-secure"/>
when-connection="not-secure secure-cert-user-overridden"/>
<description class="identity-popup-connection-secure"
value="&identity.connectionSecure;"
when-connection="secure secure-ev"/>
@ -47,14 +47,14 @@
</vbox>
<button id="identity-popup-security-expander"
class="identity-popup-expander"
when-connection="not-secure secure secure-ev"
when-connection="not-secure secure secure-ev secure-cert-user-overridden"
oncommand="gIdentityHandler.toggleSubView('security', this)"/>
</hbox>
<!-- Tracking Protection Section -->
<hbox id="tracking-protection-container"
class="identity-popup-section"
when-connection="not-secure secure secure-ev file">
when-connection="not-secure secure secure-ev secure-cert-user-overridden file">
<vbox id="tracking-protection-content" flex="1">
<description class="identity-popup-headline"
crop="end"
@ -99,7 +99,7 @@
<label observes="identity-popup-content-host"/>
<description class="identity-popup-connection-not-secure"
value="&identity.connectionNotSecure;"
when-connection="not-secure"/>
when-connection="not-secure secure-cert-user-overridden"/>
<description class="identity-popup-connection-secure"
value="&identity.connectionSecure;"
when-connection="secure secure-ev"/>
@ -115,7 +115,7 @@
<description id="identity-popup-content-supplemental"
when-connection="secure-ev"/>
<description id="identity-popup-content-verifier"
when-connection="secure secure-ev"/>
when-connection="secure secure-ev secure-cert-user-overridden"/>
<!-- Connection is Not Secure -->
<description when-connection="not-secure"

View File

@ -240,9 +240,14 @@ DistributionCustomizer.prototype = {
}
}),
_newProfile: false,
_customizationsApplied: false,
applyCustomizations: function DIST_applyCustomizations() {
this._customizationsApplied = true;
if (!Services.prefs.prefHasUserValue("browser.migration.version"))
this._newProfile = true;
if (!this._ini)
return this._checkCustomizationComplete();
@ -408,6 +413,25 @@ DistributionCustomizer.prototype = {
},
_checkCustomizationComplete: function DIST__checkCustomizationComplete() {
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
if (this._newProfile) {
let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
try {
var showPersonalToolbar = Services.prefs.getBoolPref("browser.showPersonalToolbar");
if (showPersonalToolbar) {
xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
}
} catch(e) {}
try {
var showMenubar = Services.prefs.getBoolPref("browser.showMenubar");
if (showMenubar) {
xulStore.setValue(BROWSER_DOCURL, "toolbar-menubar", "collapsed", "false");
}
} catch(e) {}
}
let prefDefaultsApplied = this._prefDefaultsApplied || !this._ini;
if (this._customizationsApplied && this._bookmarksApplied &&
prefDefaultsApplied) {

View File

@ -139,7 +139,11 @@ add_task(function* testWebNavigationFrames() {
"number of frames found should equal the number onCompleted events collected");
// ordered by frameId
let sortByFrameId = (el) => el ? el.frameId : -1;
let sortByFrameId = (el1, el2) => {
let val1 = el1 ? el1.frameId : -1;
let val2 = el2 ? el2.frameId : -1;
return val1 - val2;
};
collectedDetails = collectedDetails.sort(sortByFrameId);
getAllFramesDetails = getAllFramesDetails.sort(sortByFrameId);

View File

@ -117,9 +117,16 @@ var SessionCookiesInternal = {
restore(cookies) {
for (let cookie of cookies) {
let expiry = "expiry" in cookie ? cookie.expiry : MAX_EXPIRY;
Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
cookie.value, !!cookie.secure, !!cookie.httponly,
/* isSession = */ true, expiry);
let cookieObj = {
host: cookie.host,
path: cookie.path || "",
name: cookie.name || ""
};
if (!Services.cookies.cookieExists(cookieObj)) {
Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
cookie.value, !!cookie.secure, !!cookie.httponly,
/* isSession = */ true, expiry);
}
}
},
@ -241,6 +248,8 @@ var SessionCookiesInternal = {
if (cookie.isSession) {
CookieStore.set(cookie);
} else {
CookieStore.delete(cookie);
}
},

View File

@ -658,6 +658,15 @@ nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
L"windows.immersivecontrolpanel_cw5n1h2txyewy"
L"!microsoft.windows.immersivecontrolpanel",
L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
if (SUCCEEDED(hr)) {
// Do not check error because we could at least open
// the "Default apps" setting.
pActivator->ActivateApplication(
L"windows.immersivecontrolpanel_cw5n1h2txyewy"
L"!microsoft.windows.immersivecontrolpanel",
L"page=SettingsPageAppsDefaults"
L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid);
}
pActivator->Release();
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}

View File

@ -956,6 +956,11 @@ function loadDefaultPrefs() {
var branch = Services.prefs.getDefaultBranch("");
Services.scriptloader.loadSubScript("chrome://loop/content/preferences/prefs.js", {
pref: (key, val) => {
// If a previously set default pref exists don't overwrite it. This can
// happen for ESR or distribution.ini.
if (branch.getPrefType(key) != branch.PREF_INVALID) {
return;
}
switch (typeof val) {
case "boolean":
branch.setBoolPref(key, val);
@ -979,6 +984,9 @@ function startup(data) {
WindowListener.addonVersion = data.version;
loadDefaultPrefs();
if (!Services.prefs.getBoolPref("loop.enabled")) {
return;
}
createLoopButton();

View File

@ -821,6 +821,22 @@ const kMessageHandlers = {
reply();
},
/**
* Retrieves the Getting Started tour url.
*
* @param {Object} message Message meant for the handler function, containing
* the following parameters in its `data` property:
* [aSrc, aAdditionalParams]
* @param {Function} reply Callback function, invoked with the result of this
* message handler. The result will be sent back to
* the senders' channel.
*/
GettingStartedURL: function(message, reply) {
let aSrc = message.data[0] || null;
let aAdditionalParams = message.data[1] || {};
reply(MozLoopService.getTourURL(aSrc, aAdditionalParams).href);
},
/**
* Open the FxA profile/ settings page.
*

View File

@ -467,9 +467,15 @@ loop.panel = function (_, mozL10n) {
}));
// Open url if needed.
loop.request("getSelectedTabMetadata").then(function (metadata) {
loop.requestMulti(
["getSelectedTabMetadata"],
["GettingStartedURL", null, {}]
).then(function(results) {
var contextURL = this.props.room.decryptedContext.urls && this.props.room.decryptedContext.urls[0].location;
if (contextURL && metadata.url !== contextURL) {
contextURL = contextURL || (results[1] + "?noopenpanel=1");
if (results[0].url !== contextURL) {
loop.request("OpenURL", contextURL);
}
this.closeWindow();

View File

@ -76,6 +76,7 @@ describe("loop.panel", function() {
LogoutFromFxA: sinon.stub(),
NotifyUITour: sinon.stub(),
OpenURL: sinon.stub(),
GettingStartedURL: sinon.stub().returns("http://fakeFTUUrl.com"),
GetSelectedTabMetadata: sinon.stub().returns({}),
GetUserProfile: function() { return null; }
});
@ -829,6 +830,40 @@ describe("loop.panel", function() {
sinon.assert.calledWithExactly(openURLStub, "http://testurl.com");
});
it("should open a new tab with the FTU Getting Started URL if the room context is blank", function() {
var roomDataNoURL = {
roomToken: "QzBbvGmIZWU",
roomUrl: "http://sample/QzBbvGmIZWU",
decryptedContext: {
roomName: roomName,
urls: [{
location: ""
}]
},
maxSize: 2,
participants: [{
displayName: "Alexis",
account: "alexis@example.com",
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
}, {
displayName: "Adam",
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
}],
ctime: 1405517418
};
roomEntry = mountRoomEntry({
deleteRoom: sandbox.stub(),
isOpenedRoom: false,
room: new loop.store.Room(roomDataNoURL)
});
var ftuURL = requestStubs.GettingStartedURL() + "?noopenpanel=1";
TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
sinon.assert.calledOnce(openURLStub);
sinon.assert.calledWithExactly(openURLStub, ftuURL);
});
it("should not open a new tab if the context is the same as the currently open tab", function() {
LoopMochaUtils.stubLoopRequest({
GetSelectedTabMetadata: function() {

View File

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
var [, gHandlers] = LoopAPI.inspect();
add_task(function* test_mozLoop_gettingStartedURL() {
let expectedURL = MozLoopService.getTourURL().href;
// Test gettingStartedURL
gHandlers.GettingStartedURL({ data: [] }, result => {
Assert.equal(result, expectedURL, "should get mozLoopService GettingStartedURL value correctly");
});
});

View File

@ -5,6 +5,7 @@ firefox-appdir = browser
skip-if = toolkit == 'gonk'
[test_loopapi_doNotDisturb.js]
[test_loopapi_ftu_url.js]
[test_loopapi_internal.js]
[test_loopapi_prefs.js]
[test_looppush_initialize.js]

View File

@ -41,6 +41,10 @@ const PREFS = {
function setDefaultPrefs() {
let branch = Services.prefs.getDefaultBranch(PREF_BRANCH);
for (let [key, val] in Iterator(PREFS)) {
// If someone beat us to setting a default, don't overwrite it. This can
// happen if distribution.ini sets the default first.
if (branch.getPrefType(key) != branch.PREF_INVALID)
continue;
switch (typeof val) {
case "boolean":
branch.setBoolPref(key, val);
@ -519,8 +523,7 @@ function startup(data, reason) {
}
// watch pref change and enable/disable if necessary
Services.prefs.addObserver("extensions.pocket.enabled", prefObserver, false);
if (Services.prefs.prefHasUserValue("extensions.pocket.enabled") &&
!Services.prefs.getBoolPref("extensions.pocket.enabled"))
if (!Services.prefs.getBoolPref("extensions.pocket.enabled"))
return;
PocketOverlay.startup(reason);
});

View File

@ -375,8 +375,8 @@
@RESPATH@/browser/components/nsBrowserGlue.js
@RESPATH@/browser/components/nsSetDefaultBrowser.manifest
@RESPATH@/browser/components/nsSetDefaultBrowser.js
@RESPATH@/browser/components/devtools-clhandler.manifest
@RESPATH@/browser/components/devtools-clhandler.js
@RESPATH@/browser/components/devtools-startup.manifest
@RESPATH@/browser/components/devtools-startup.js
@RESPATH@/browser/components/webideCli.js
@RESPATH@/browser/components/webideComponents.manifest
@RESPATH@/browser/components/Experiments.manifest

View File

@ -6,7 +6,6 @@ support-files =
[browser_ProcessHangNotifications.js]
skip-if = !e10s
[browser_ContentSearch.js]
skip-if = e10s
support-files =
contentSearch.js
contentSearchBadImage.xml

View File

@ -97,13 +97,15 @@ add_task(function* search() {
healthReportKey: "ContentSearchTest",
searchPurpose: "ContentSearchTest",
};
let submissionURL =
engine.getSubmission(data.searchString, "", data.whence).uri.spec;
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "Search",
data: data,
expectedURL: submissionURL,
});
let submissionURL =
engine.getSubmission(data.searchString, "", data.whence).uri.spec;
yield waitForLoadAndStopIt(gBrowser.selectedBrowser, submissionURL);
let msg = yield waitForTestMsg("loadStopped");
Assert.equal(msg.data.url, submissionURL, "Correct search page loaded");
});
add_task(function* searchInBackgroundTab() {
@ -120,18 +122,20 @@ add_task(function* searchInBackgroundTab() {
healthReportKey: "ContentSearchTest",
searchPurpose: "ContentSearchTest",
};
let submissionURL =
engine.getSubmission(data.searchString, "", data.whence).uri.spec;
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "Search",
data: data,
expectedURL: submissionURL,
});
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
registerCleanupFunction(() => gBrowser.removeTab(newTab));
let submissionURL =
engine.getSubmission(data.searchString, "", data.whence).uri.spec;
yield waitForLoadAndStopIt(searchBrowser, submissionURL);
let msg = yield waitForTestMsg("loadStopped");
Assert.equal(msg.data.url, submissionURL, "Correct search page loaded");
});
add_task(function* badImage() {
@ -331,33 +335,6 @@ function waitForNewEngine(basename, numImages) {
return Promise.all([addDeferred.promise].concat(eventPromises));
}
function waitForLoadAndStopIt(browser, expectedURL) {
let deferred = Promise.defer();
let listener = {
onStateChange: function (webProg, req, flags, status) {
if (req instanceof Ci.nsIChannel) {
let url = req.originalURI.spec;
info("onStateChange " + url);
let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
Ci.nsIWebProgressListener.STATE_START;
if ((flags & docStart) && webProg.isTopLevel && url == expectedURL) {
browser.removeProgressListener(listener);
ok(true, "Expected URL loaded");
req.cancel(Components.results.NS_ERROR_FAILURE);
deferred.resolve();
}
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
]),
};
browser.addProgressListener(listener);
info("Waiting for URL to load: " + expectedURL);
return deferred.promise;
}
function addTab() {
let deferred = Promise.defer();
let tab = gBrowser.addTab();

View File

@ -23,4 +23,42 @@ addMessageListener(TEST_MSG, msg => {
detail: msg.data,
})
);
// If the message is a search, stop the page from loading and then tell the
// test that it loaded.
if (msg.data.type == "Search") {
waitForLoadAndStopIt(msg.data.expectedURL, url => {
sendAsyncMessage(TEST_MSG, {
type: "loadStopped",
url: url,
});
});
}
});
function waitForLoadAndStopIt(expectedURL, callback) {
let Ci = Components.interfaces;
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let listener = {
onStateChange: function (webProg, req, flags, status) {
if (req instanceof Ci.nsIChannel) {
let url = req.originalURI.spec;
dump("waitForLoadAndStopIt: onStateChange " + url + "\n");
let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
Ci.nsIWebProgressListener.STATE_START;
if ((flags & docStart) && webProg.isTopLevel && url == expectedURL) {
webProgress.removeProgressListener(listener);
req.cancel(Components.results.NS_ERROR_FAILURE);
callback(url);
}
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
]),
};
webProgress.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
dump("waitForLoadAndStopIt: Waiting for URL to load: " + expectedURL + "\n");
}

View File

@ -11,6 +11,7 @@
/* Show the right elements for the right connection states. */
#identity-popup[connection=not-secure] [when-connection~=not-secure],
#identity-popup[connection=secure-cert-user-overridden] [when-connection~=secure-cert-user-overridden],
#identity-popup[connection=secure-ev] [when-connection~=secure-ev],
#identity-popup[connection=secure] [when-connection~=secure],
#identity-popup[connection=chrome] [when-connection~=chrome],
@ -234,6 +235,11 @@
background-image: url(chrome://browser/skin/controlcenter/conn-degraded.svg);
}
#identity-popup[connection=secure-cert-user-overridden] #identity-popup-securityView,
#identity-popup[connection=secure-cert-user-overridden] #identity-popup-security-content {
background-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
}
#identity-popup[loginforms=insecure] #identity-popup-securityView,
#identity-popup[loginforms=insecure] #identity-popup-security-content,
#identity-popup[mixedcontent~=active-loaded][isbroken] #identity-popup-securityView,

View File

@ -198,6 +198,7 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
overflow: visible;
}
.cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent,
#PanelUI-popup > .panel-arrowcontainer > .panel-arrowcontent {
overflow: hidden;
}

View File

@ -166,6 +166,7 @@
}
#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.certUserOverridden > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon,
#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon {
list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);

View File

@ -33,8 +33,7 @@ function shouldCapture() {
// Automation isn't able to schedule test jobs to only run on nightlies so we handle it here
// (see also: bug 1116275).
let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" ||
AppConstants.SOURCE_REVISION_URL == "" ||
AppConstants.SOURCE_REVISION_URL == "1"; // bug 1248027
AppConstants.SOURCE_REVISION_URL == "";
if (!capture) {
ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO");
}

View File

@ -44,7 +44,7 @@ this.TestRunner = {
_libDir: null,
init(extensionPath) {
log.info("init");
log.debug("init");
this._extensionPath = extensionPath;
},

View File

@ -126,6 +126,7 @@ var getServerTraits = Task.async(function*(target) {
*/
var AnimationsController = {
PLAYERS_UPDATED_EVENT: "players-updated",
ALL_ANIMATIONS_TOGGLED_EVENT: "all-animations-toggled",
initialize: Task.async(function*() {
if (this.initialized) {
@ -255,7 +256,9 @@ var AnimationsController = {
return promise.resolve();
}
return this.animationsFront.toggleAll().catch(e => console.error(e));
return this.animationsFront.toggleAll()
.then(() => this.emit(this.ALL_ANIMATIONS_TOGGLED_EVENT, this))
.catch(e => console.error(e));
},
/**

View File

@ -52,10 +52,10 @@ var AnimationsPanel = {
}
// Binding functions that need to be called in scope.
for (let functionName of ["onPickerStarted", "onPickerStopped",
"refreshAnimationsUI", "toggleAll", "onTabNavigated",
"onTimelineDataChanged", "playPauseTimeline", "rewindTimeline",
"onRateChanged"]) {
for (let functionName of ["onKeyDown", "onPickerStarted",
"onPickerStopped", "refreshAnimationsUI", "onToggleAllClicked",
"onTabNavigated", "onTimelineDataChanged", "onTimelinePlayClicked",
"onTimelineRewindClicked", "onRateChanged"]) {
this[functionName] = this[functionName].bind(this);
}
let hUtils = gToolbox.highlighterUtils;
@ -114,9 +114,13 @@ var AnimationsPanel = {
gToolbox.on("picker-started", this.onPickerStarted);
gToolbox.on("picker-stopped", this.onPickerStopped);
this.toggleAllButtonEl.addEventListener("click", this.toggleAll);
this.playTimelineButtonEl.addEventListener("click", this.playPauseTimeline);
this.rewindTimelineButtonEl.addEventListener("click", this.rewindTimeline);
this.toggleAllButtonEl.addEventListener("click", this.onToggleAllClicked);
this.playTimelineButtonEl.addEventListener(
"click", this.onTimelinePlayClicked);
this.rewindTimelineButtonEl.addEventListener(
"click", this.onTimelineRewindClicked);
document.addEventListener("keydown", this.onKeyDown, false);
gToolbox.target.on("navigate", this.onTabNavigated);
@ -136,9 +140,13 @@ var AnimationsPanel = {
gToolbox.off("picker-started", this.onPickerStarted);
gToolbox.off("picker-stopped", this.onPickerStopped);
this.toggleAllButtonEl.removeEventListener("click", this.toggleAll);
this.playTimelineButtonEl.removeEventListener("click", this.playPauseTimeline);
this.rewindTimelineButtonEl.removeEventListener("click", this.rewindTimeline);
this.toggleAllButtonEl.removeEventListener("click", this.onToggleAllClicked);
this.playTimelineButtonEl.removeEventListener(
"click", this.onTimelinePlayClicked);
this.rewindTimelineButtonEl.removeEventListener(
"click", this.onTimelineRewindClicked);
document.removeEventListener("keydown", this.onKeyDown, false);
gToolbox.target.off("navigate", this.onTabNavigated);
@ -150,6 +158,22 @@ var AnimationsPanel = {
}
},
onKeyDown: function(event) {
let keyEvent = Ci.nsIDOMKeyEvent;
// If the space key is pressed, it should toggle the play state of
// the animations displayed in the panel, or of all the animations on
// the page if the selected node does not have any animation on it.
if (event.keyCode === keyEvent.DOM_VK_SPACE) {
if (AnimationsController.animationPlayers.length > 0) {
this.playPauseTimeline().catch(ex => console.error(ex));
} else {
this.toggleAll().catch(ex => console.error(ex));
}
event.preventDefault();
}
},
togglePlayers: function(isVisible) {
if (isVisible) {
document.body.removeAttribute("empty");
@ -168,32 +192,53 @@ var AnimationsPanel = {
this.pickerButtonEl.removeAttribute("checked");
},
onToggleAllClicked: function() {
this.toggleAll().catch(ex => console.error(ex));
},
/**
* Toggle (pause/play) all animations in the current target
* and update the UI the toggleAll button.
*/
toggleAll: Task.async(function*() {
this.toggleAllButtonEl.classList.toggle("paused");
yield AnimationsController.toggleAll();
}),
onTimelinePlayClicked: function() {
this.playPauseTimeline().catch(ex => console.error(ex));
},
/**
* Depending on the state of the timeline either pause or play the animations
* displayed in it.
* If the animations are finished, this will play them from the start again.
* If the animations are playing, this will pause them.
* If the animations are paused, this will resume them.
*
* @return {Promise} Resolves when the playState is changed and the UI
* is refreshed
*/
playPauseTimeline: function() {
AnimationsController.toggleCurrentAnimations(this.timelineData.isMoving)
.then(() => this.refreshAnimationsStateAndUI())
.catch(e => console.error(e));
return AnimationsController
.toggleCurrentAnimations(this.timelineData.isMoving)
.then(() => this.refreshAnimationsStateAndUI());
},
onTimelineRewindClicked: function() {
this.rewindTimeline().catch(ex => console.error(ex));
},
/**
* Reset the startTime of all current animations shown in the timeline and
* pause them.
*
* @return {Promise} Resolves when currentTime is set and the UI is refreshed
*/
rewindTimeline: function() {
AnimationsController.setCurrentTimeAll(0, true)
.then(() => this.refreshAnimationsStateAndUI())
.catch(e => console.error(e));
return AnimationsController
.setCurrentTimeAll(0, true)
.then(() => this.refreshAnimationsStateAndUI());
},
/**
@ -203,7 +248,7 @@ var AnimationsPanel = {
onRateChanged: function(e, rate) {
AnimationsController.setPlaybackRateAll(rate)
.then(() => this.refreshAnimationsStateAndUI())
.catch(e => console.error(e));
.catch(ex => console.error(ex));
},
onTabNavigated: function() {

View File

@ -31,6 +31,8 @@ skip-if = os == "linux" && !debug # Bug 1227792
[browser_animation_running_on_compositor.js]
[browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
[browser_animation_shows_player_on_valid_node.js]
[browser_animation_spacebar_toggles_animations.js]
[browser_animation_spacebar_toggles_node_animations.js]
[browser_animation_target_highlight_select.js]
[browser_animation_target_highlighter_lock.js]
[browser_animation_timeline_currentTime.js]

View File

@ -0,0 +1,43 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the spacebar key press toggles the toggleAll button state
// when a node with no animation is selected.
// This test doesn't need to test if animations actually pause/resume
// because there's an other test that does this :
// browser_animation_toggle_button_toggles_animation.js
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel, inspector, window, controller} = yield openAnimationInspector();
let {toggleAllButtonEl} = panel;
// select a node without animations
yield selectNode(".still", inspector);
// ensure the focus is on the animation panel
window.focus();
info("Simulate spacebar stroke and check toggleAll button" +
" is in paused state");
// sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
let onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
EventUtils.sendKey("SPACE", window);
yield onToggled;
ok(toggleAllButtonEl.classList.contains("paused"),
"The toggle all button is in its paused state");
info("Simulate spacebar stroke and check toggleAll button" +
" is in playing state");
// sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
EventUtils.sendKey("SPACE", window);
yield onToggled;
ok(!toggleAllButtonEl.classList.contains("paused"),
"The toggle all button is in its playing state again");
});

View File

@ -0,0 +1,40 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the spacebar key press toggles the play/resume button state.
// This test doesn't need to test if animations actually pause/resume
// because there's an other test that does this.
// There are animations in the test page and since, by default, the <body> node
// is selected, animations will be displayed in the timeline, so the timeline
// play/resume button will be displayed
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel, window} = yield openAnimationInspector();
let {playTimelineButtonEl} = panel;
// ensure the focus is on the animation panel
window.focus();
info("Simulate spacebar stroke and check playResume button" +
" is in paused state");
// sending the key will lead to a UI_UPDATE_EVENT
let onUpdated = panel.once(panel.UI_UPDATED_EVENT);
EventUtils.sendKey("SPACE", window);
yield onUpdated;
ok(playTimelineButtonEl.classList.contains("paused"),
"The play/resume button is in its paused state");
info("Simulate spacebar stroke and check playResume button" +
" is in playing state");
// sending the key will lead to a UI_UPDATE_EVENT
onUpdated = panel.once(panel.UI_UPDATED_EVENT);
EventUtils.sendKey("SPACE", window);
yield onUpdated;
ok(!playTimelineButtonEl.classList.contains("paused"),
"The play/resume button is in its play state again");
});

View File

@ -6,6 +6,15 @@
bug 1242893 is fixed */
/* globals BrowserToolboxProcess */
/**
* This XPCOM component is loaded very early.
* It handles command line arguments like -jsconsole, but also ensures starting
* core modules like devtools/devtools-browser that listen for application
* startup.
*
* Be careful to lazy load dependencies as much as possible.
**/
"use strict";
const { interfaces: Ci, utils: Cu } = Components;
@ -16,9 +25,9 @@ const kDebuggerPrefs = [
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
function devtoolsCommandlineHandler() {}
function DevToolsStartup() {}
devtoolsCommandlineHandler.prototype = {
DevToolsStartup.prototype = {
handle: function(cmdLine) {
let consoleFlag = cmdLine.handleFlag("jsconsole", false);
let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
@ -30,9 +39,6 @@ devtoolsCommandlineHandler.prototype = {
if (debuggerFlag) {
this.handleDebuggerFlag(cmdLine);
}
if (devtoolsFlag) {
this.handleDevToolsFlag();
}
let debuggerServerFlag;
try {
debuggerServerFlag =
@ -45,19 +51,36 @@ devtoolsCommandlineHandler.prototype = {
if (debuggerServerFlag) {
this.handleDebuggerServerFlag(cmdLine, debuggerServerFlag);
}
let onStartup = function(window) {
Services.obs.removeObserver(onStartup,
"browser-delayed-startup-finished");
// Ensure loading core module once firefox is ready
this.initDevTools();
if (devtoolsFlag) {
this.handleDevToolsFlag(window);
}
}.bind(this);
Services.obs.addObserver(onStartup, "browser-delayed-startup-finished",
false);
},
initDevTools: function() {
let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
// Ensure loading main devtools module that hooks up into browser UI
// and initialize all devtools machinery.
// browser.xul or main top-level document used to load this module,
// but this code may be called without/before it.
require("devtools/client/framework/devtools-browser");
},
handleConsoleFlag: function(cmdLine) {
let window = Services.wm.getMostRecentWindow("devtools:webconsole");
if (!window) {
let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
// Ensure loading main devtools module that hooks up into browser UI
// and initialize all devtools machinery.
// browser.xul or main top-level document used to load this module,
// but this code may be called without/before it.
// Bug 1247203 should ease handling this.
require("devtools/client/framework/devtools-browser");
this.initDevTools();
let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
let hudservice = require("devtools/client/webconsole/hudservice");
let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
hudservice.toggleBrowserConsole().then(null, console.error);
@ -72,16 +95,12 @@ devtoolsCommandlineHandler.prototype = {
},
// Open the toolbox on the selected tab once the browser starts up.
handleDevToolsFlag: function() {
Services.obs.addObserver(function onStartup(window) {
Services.obs.removeObserver(onStartup,
"browser-delayed-startup-finished");
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {gDevTools} = require("devtools/client/framework/devtools");
const {TargetFactory} = require("devtools/client/framework/target");
let target = TargetFactory.forTab(window.gBrowser.selectedTab);
gDevTools.showToolbox(target);
}, "browser-delayed-startup-finished", false);
handleDevToolsFlag: function(window) {
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {gDevTools} = require("devtools/client/framework/devtools");
const {TargetFactory} = require("devtools/client/framework/target");
let target = TargetFactory.forTab(window.gBrowser.selectedTab);
gDevTools.showToolbox(target);
},
_isRemoteDebuggingEnabled() {
@ -168,4 +187,4 @@ devtoolsCommandlineHandler.prototype = {
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
[devtoolsCommandlineHandler]);
[DevToolsStartup]);

View File

@ -1,2 +1,2 @@
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-clhandler.js
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
contract @mozilla.org/toolkit/console-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}

View File

@ -4,6 +4,14 @@
"use strict";
/**
* This is the main module loaded in Firefox desktop that handles browser
* windows and coordinates devtools around each window.
*
* This module is loaded lazily by devtools-clhandler.js, once the first
* browser window is ready (i.e. fired browser-delayed-startup-finished event)
**/
const {Cc, Ci, Cu} = require("chrome");
const Services = require("Services");
const promise = require("promise");
@ -116,10 +124,17 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
},
observe: function(subject, topic, prefName) {
if (prefName.endsWith("enabled")) {
for (let win of this._trackedBrowserWindows) {
this.updateCommandAvailability(win);
}
switch (topic) {
case "browser-delayed-startup-finished":
this._registerBrowserWindow(subject);
break;
case "nsPref:changed":
if (prefName.endsWith("enabled")) {
for (let win of this._trackedBrowserWindows) {
this.updateCommandAvailability(win);
}
}
break;
}
},
@ -319,11 +334,11 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
* @param {XULDocument} doc
* The document to which menuitems and handlers are to be added
*/
// Used by browser.js
registerBrowserWindow: function DT_registerBrowserWindow(win) {
_registerBrowserWindow: function(win) {
this.updateCommandAvailability(win);
this.ensurePrefObserver();
gDevToolsBrowser._trackedBrowserWindows.add(win);
win.addEventListener("unload", this);
gDevToolsBrowser._addAllToolsToMenu(win.document);
if (this._isFirebugInstalled()) {
@ -749,8 +764,9 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
* @param {XULWindow} win
* The window containing the menu entry
*/
forgetBrowserWindow: function DT_forgetBrowserWindow(win) {
_forgetBrowserWindow: function(win) {
gDevToolsBrowser._trackedBrowserWindows.delete(win);
win.removeEventListener("unload", this);
// Destroy toolboxes for closed window
for (let [target, toolbox] of gDevTools._toolboxes) {
@ -792,6 +808,11 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
break;
case "TabSelect":
gDevToolsBrowser._updateMenuCheckbox();
break;
case "unload":
// top-level browser window unload
gDevToolsBrowser._forgetBrowserWindow(event.target.defaultView);
break;
}
},
@ -800,7 +821,12 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
*/
destroy: function() {
Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
gDevToolsBrowser._forgetBrowserWindow(win);
}
},
}
@ -820,6 +846,16 @@ gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished", false);
// Fake end of browser window load event for all already opened windows
// that is already fully loaded.
let enumerator = Services.wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {
gDevToolsBrowser._registerBrowserWindow(win);
}
}
// Load the browser devtools main module as the loader's main module.
// This is done precisely here as main.js ends up dispatching the

View File

@ -139,7 +139,7 @@
tooltiptext="&toolboxCloseButton.tooltip;"/>
</hbox>
</toolbar>
<vbox flex="1">
<vbox flex="1" class="theme-body">
<!-- Set large flex to allow the toolbox-panel-webconsole to have a
height set to a small value without flexing to fill up extra
space. There must be a flex on both to ensure that the console

View File

@ -44,8 +44,8 @@ if CONFIG['MOZ_BUILD_APP'] == 'browser':
DIRS += ['themes/shims']
EXTRA_COMPONENTS += [
'devtools-clhandler.js',
'devtools-clhandler.manifest',
'devtools-startup.js',
'devtools-startup.manifest',
]
JAR_MANIFESTS += ['jar.mn']

View File

@ -27,26 +27,17 @@ xul|scrollbar[orient="horizontal"] {
max-height: 10px;
}
:root[platform="mac"] xul|slider {
-moz-appearance: none !important;
}
:root[platform="mac"] xul|thumb {
-moz-appearance: none !important;
border-radius: 3px;
}
xul|scrollbar xul|thumb {
background-color: rgba(170,170,170,0.2) !important;
}
:root[platform="win"] xul|thumb,
:root[platform="linux"] xul|thumb {
-moz-appearance: none !important;
border-width: 0px !important;
border-radius: 3px !important;
}
:root[platform="mac"] xul|slider {
-moz-appearance: none !important;
}
:root[platform="win"] xul|scrollbar xul|scrollbarbutton,
:root[platform="linux"] xul|scrollbar xul|scrollbarbutton,
:root[platform="win"] xul|scrollbar xul|gripper,

View File

@ -832,7 +832,7 @@
-moz-box-align: center;
min-width: 32px;
min-height: 24px;
max-width: 95px;
max-width: 100px;
margin: 0;
padding: 0;
border-style: solid;

View File

@ -1349,7 +1349,7 @@ Messages.JavaScriptEvalOutput = function(evalResponse, errorMessage)
// be useful to extensions customizing the console output.
this.response = evalResponse;
if (errorMessage) {
if (typeof(errorMessage) !== "undefined") {
severity = "error";
msg = errorMessage;
quoteStrings = false;

View File

@ -308,6 +308,11 @@ JSTerm.prototype = {
return;
}
let errorMessage = response.exceptionMessage;
// Wrap thrown strings in Error objects, so `throw "foo"` outputs
// "Error: foo"
if (typeof(response.exception) === "string") {
errorMessage = new Error(errorMessage).toString();
}
let result = response.result;
let helperResult = response.helperResult;
let helperHasRawOutput = !!(helperResult || {}).rawOutput;

View File

@ -11,7 +11,7 @@
// On e10s, the exception is triggered in child process
// and is ignored by test harness
if (!Services.appinfo.browserTabsRemoteAutostart) {
expectUncaughtException();
SimpleTest.ignoreAllUncaughtExceptions();
}
function test() {
@ -49,6 +49,37 @@ function test() {
severity: SEVERITY_ERROR,
collapsible: true,
stacktrace: stack,
}, {
text: "An invalid or illegal string was specified",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
collapsible: true,
stacktrace: [{
file: TEST_FILE,
fn: "domAPI",
line: 25,
}, {
file: TEST_FILE,
fn: "onLoadDomAPI",
line: 33,
}
]
}, {
text: "DOMException",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
collapsible: true,
stacktrace: [{
file: TEST_FILE,
fn: "domException",
line: 29,
}, {
file: TEST_FILE,
fn: "onLoadDomException",
line: 36,
},
]
}],
});

View File

@ -142,4 +142,28 @@ function* testJSTerm(hud) {
jsterm.clearOutput();
yield jsterm.execute("undefined");
yield checkResult("undefined", "undefined is printed");
// check that thrown strings produce error messages,
// and the message text matches that of a stringified error object
// bug 1099071
jsterm.clearOutput();
yield jsterm.execute("throw '';");
yield checkResult((node) => {
return node.parentNode.getAttribute("severity") === "error" &&
node.textContent === new Error("").toString();
}, "thrown empty string generates error message");
jsterm.clearOutput();
yield jsterm.execute("throw 'tomatoes';");
yield checkResult((node) => {
return node.parentNode.getAttribute("severity") === "error" &&
node.textContent === new Error("tomatoes").toString();
}, "thrown non-empty string generates error message");
jsterm.clearOutput();
yield jsterm.execute("throw { foo: 'bar' };");
yield checkResult((node) => {
return node.parentNode.getAttribute("severity") === "error" &&
node.textContent === Object.prototype.toString();
}, "thrown object generates error message");
}

View File

@ -21,7 +21,20 @@
nonExistingMethodCall();
}
window.onload = firstCall;
function domAPI() {
document.querySelector("buggy;selector");
}
function domException() {
throw new DOMException("DOMException");
}
window.addEventListener("load", firstCall);
window.addEventListener("load", function onLoadDomAPI() {
domAPI();
});
window.addEventListener("load", function onLoadDomException() {
domException();
});
</script>
</head>
<body>

View File

@ -1,5 +1,5 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -7,7 +7,7 @@
"use strict";
const Services = require("Services");
const { Cc, Ci, Cu, components, ChromeWorker } = require("chrome");
const { Cc, Ci, Cu, Cr, components, ChromeWorker } = require("chrome");
const { ActorPool, OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
const { BreakpointActor } = require("devtools/server/actors/breakpoint");
const { FrameActor } = require("devtools/server/actors/frame");
@ -1846,10 +1846,17 @@ ThreadActor.prototype = {
return undefined;
}
// NS_ERROR_NO_INTERFACE exceptions are a special case in browser code,
// since they're almost always thrown by QueryInterface functions, and
// handled cleanly by native code.
if (aValue == Cr.NS_ERROR_NO_INTERFACE) {
return undefined;
}
const generatedLocation = this.sources.getFrameLocation(aFrame);
const { sourceActor } = this.unsafeSynchronize(this.sources.getOriginalLocation(
const { originalSourceActor } = this.unsafeSynchronize(this.sources.getOriginalLocation(
generatedLocation));
const url = sourceActor ? sourceActor.url : null;
const url = originalSourceActor ? originalSourceActor.url : null;
if (this.sources.isBlackBoxed(url)) {
return undefined;

View File

@ -73,8 +73,8 @@ function test_black_box_exception() {
gThreadClient.pauseOnExceptions(true);
gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
do_check_neq(aPacket.frame.where.url, BLACK_BOXED_URL,
"We shouldn't pause while in the black boxed source.");
do_check_eq(aPacket.frame.where.source.url, SOURCE_URL,
"We shouldn't pause while in the black boxed source.");
finishClient(gClient);
});

View File

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that the debugger automatically ignores NS_ERROR_NO_INTERFACE
* exceptions, but not normal ones.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test()
{
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-no-interface");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect().then(function() {
attachTestTabAndResume(gClient, "test-no-interface", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
test_pause_frame();
});
});
do_test_pending();
}
function test_pause_frame()
{
gThreadClient.pauseOnExceptions(true, false, function () {
gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
do_check_eq(aPacket.why.type, "exception");
do_check_eq(aPacket.why.exception, 42);
gThreadClient.resume(function () {
finishClient(gClient);
});
});
gDebuggee.eval("(" + function() {
function QueryInterface() {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
function stopMe() {
throw 42;
};
try {
QueryInterface();
} catch (e) {}
try {
stopMe();
} catch (e) {}
} + ")()");
});
}

View File

@ -239,6 +239,7 @@ reason = bug 820380
[test_unsafeDereference.js]
[test_add_actors.js]
[test_ignore_caught_exceptions.js]
[test_ignore_no_interface_exceptions.js]
[test_requestTypes.js]
reason = bug 937197
[test_layout-reflows-observer.js]

View File

@ -52,6 +52,8 @@
#include "nsGlobalWindow.h"
#include "nsScriptNameSpaceManager.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/ErrorEvent.h"
#include "nsAXPCNativeCallContext.h"
#include "mozilla/CycleCollectedJSRuntime.h"
@ -223,6 +225,41 @@ ProcessNameForCollectorLog()
"default" : "content";
}
// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC Exceptions.
//
// Note that the returned object is _not_ wrapped into the compartment of cx.
static JSObject*
FindExceptionStack(JSContext* cx, JS::HandleObject exceptionObject)
{
JSAutoCompartment ac(cx, exceptionObject);
JS::RootedObject stackObject(cx, ExceptionStackOrNull(cx, exceptionObject));
if (stackObject) {
return stackObject;
}
// It is not a JS Exception, try DOM Exception.
RefPtr<Exception> exception;
UNWRAP_OBJECT(DOMException, exceptionObject, exception);
if (!exception) {
// Not a DOM Exception, try XPC Exception.
UNWRAP_OBJECT(Exception, exceptionObject, exception);
if (!exception) {
return nullptr;
}
}
nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
if (!stack) {
return nullptr;
}
JS::RootedValue value(cx);
stack->GetNativeSavedFrame(&value);
if (value.isObject()) {
stackObject = &value.toObject();
}
return stackObject;
}
static PRTime
GetCollectionTimeDelta()
{
@ -426,7 +463,7 @@ public:
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> exObj(cx, mError.toObjectOrNull());
JS::RootedObject stack(cx, ExceptionStackOrNull(cx, exObj));
JS::RootedObject stack(cx, FindExceptionStack(cx, exObj));
mReport->LogToConsoleWithStack(stack);
} else {
mReport->LogToConsole();
@ -512,7 +549,7 @@ SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
if (exception.isObject()) {
JS::RootedObject exObj(cx, exception.toObjectOrNull());
JSAutoCompartment ac(cx, exObj);
JS::RootedObject stackVal(cx, ExceptionStackOrNull(cx, exObj));
JS::RootedObject stackVal(cx, FindExceptionStack(cx, exObj));
xpcReport->LogToConsoleWithStack(stackVal);
} else {
xpcReport->LogToConsole();

View File

@ -238,7 +238,9 @@ task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndReso
// entities. (Each locale produces its own .dtd file, backstopped by the en-US
// .dtd file in tree.) Android Studio (and IntelliJ) don't handle these inline
// entities smoothly. This filter merely expands the entities in place, making
// them appear properly throughout the IDE.
// them appear properly throughout the IDE. Be aware that this assumes that the
// JVM's file.encoding is utf-8. See comments in
// mobile/android/mach_commands.py.
class ExpandXMLEntitiesFilter extends FilterReader {
ExpandXMLEntitiesFilter(Reader input) {
// Extremely inefficient, but whatever.

View File

@ -211,7 +211,6 @@ ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
.gradle.deps: .aapt.deps FORCE
@$(TOUCH) $@
$(topsrcdir)/mach gradle \
$(if $(MOZILLA_OFFICIAL),--no-daemon --offline --info) \
app:assembleAutomationDebug app:assembleAutomationDebugAndroidTest -x lint
classes.dex: .gradle.deps

View File

@ -161,6 +161,7 @@ import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.Vector;
import java.util.regex.Pattern;
public class BrowserApp extends GeckoApp
implements TabsPanel.TabsLayoutChangeListener,
@ -2433,6 +2434,11 @@ public class BrowserApp extends GeckoApp
return;
}
// Filter out URLs and long suggestions
if (query.length() > 50 || Pattern.matches("^(https?|ftp|file)://.*", query)) {
return;
}
final GeckoProfile profile = getProfile();
// Don't bother storing search queries in guest mode
if (profile.inGuestMode()) {

View File

@ -39,7 +39,9 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
class SearchEngineRow extends AnimatedHeightLayout {
// Duration for fade-in animation
@ -383,6 +385,18 @@ class SearchEngineRow extends AnimatedHeightLayout {
// Remove duplicates of search engine suggestions from saved searches.
List<String> searchHistorySuggestions = (rawSearchHistorySuggestions != null) ? rawSearchHistorySuggestions : new ArrayList<String>();
// Filter out URLs and long search suggestions
Iterator<String> searchistoryIterator = searchHistorySuggestions.iterator();
while (searchistoryIterator.hasNext()) {
final String currentSearchHistory = searchistoryIterator.next();
if (currentSearchHistory.length() > 50 || Pattern.matches("^(https?|ftp|file)://.*", currentSearchHistory)) {
searchHistorySuggestions.remove(currentSearchHistory);
}
}
List<String> searchEngineSuggestions = new ArrayList<String>();
for (String suggestion : searchEngine.getSuggestions()) {
searchHistorySuggestions.remove(suggestion);

View File

@ -2283,6 +2283,11 @@ var NativeWindow = {
aButtons = [];
}
if (aButtons.length > 2) {
console.log("Doorhanger can have a maximum of two buttons!");
aButtons.length = 2;
}
aButtons.forEach((function(aButton) {
this._callbacks[this._callbacksId] = { cb: aButton.callback, prompt: this._promptId };
aButton.callback = this._callbacksId;

View File

@ -38,5 +38,13 @@
"unpack": true,
"digest": "ef1d0038da879cc6840fced87671f8f6a18c51375498804f64d21fa48d7089ded4da2be36bd06a1457083e9110e59c0884f1e074dc609d29617c131caea8f234",
"size": 50542140
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "dotgradle.tar.xz",
"unpack": true,
"digest": "9f082ccd71ad18991eb71fcad355c6990f50a72a09ab9b79696521485656083a72faf5a8d4714de9c4b901ee2319b6786a51964846bb7075061642a8505501c2",
"size": 512
}
]

View File

@ -57,6 +57,12 @@ class MachCommands(MachCommandBase):
# Avoid logging the command
self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
# In automation, JAVA_HOME is set via mozconfig, which needs
# to be specially handled in each mach command. This turns
# $JAVA_HOME/bin/java into $JAVA_HOME.
java_home = os.path.dirname(os.path.dirname(self.substs['JAVA']))
# We force the Gradle JVM to run with the UTF-8 encoding, since we
# filter strings.xml, which is really UTF-8; the ellipsis character is
# replaced with ??? in some encodings (including ASCII). It's not yet
@ -67,7 +73,10 @@ class MachCommands(MachCommandBase):
# http://tools.android.com/knownissues/encoding. See
# http://stackoverflow.com/a/21267635 for discussion of this approach.
return self.run_process([self.substs['GRADLE']] + args,
append_env={'GRADLE_OPTS': '-Dfile.encoding=utf-8'},
append_env={
'GRADLE_OPTS': '-Dfile.encoding=utf-8',
'JAVA_HOME': java_home,
},
pass_thru=True, # Allow user to run gradle interactively.
ensure_exit_code=False, # Don't throw on non-zero exit code.
cwd=mozpath.join(self.topsrcdir))

View File

@ -293,18 +293,29 @@ public class AndroidFxAccount {
}
public String getProfileServerURI() {
return accountManager.getUserData(account, ACCOUNT_KEY_PROFILE_SERVER);
String profileURI = accountManager.getUserData(account, ACCOUNT_KEY_PROFILE_SERVER);
if (profileURI == null) {
if (isStaging()) {
return FxAccountConstants.STAGE_PROFILE_SERVER_ENDPOINT;
}
return FxAccountConstants.DEFAULT_PROFILE_SERVER_ENDPOINT;
}
return profileURI;
}
public String getOAuthServerURI() {
// Allow testing against stage.
if (FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT.equals(getAccountServerURI())) {
if (isStaging()) {
return FxAccountConstants.STAGE_OAUTH_SERVER_ENDPOINT;
} else {
return FxAccountConstants.DEFAULT_OAUTH_SERVER_ENDPOINT;
}
}
private boolean isStaging() {
return FxAccountConstants.STAGE_AUTH_SERVER_ENDPOINT.equals(getAccountServerURI());
}
private String constructPrefsPath(String product, long version, String extra) throws GeneralSecurityException, UnsupportedEncodingException {
String profile = getProfile();
String username = account.name;

View File

@ -5,22 +5,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsNSSCallbacks.h"
#include "pkix/pkixtypes.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "nsNSSComponent.h"
#include "nsNSSIOLayer.h"
#include "nsIWebProgressListener.h"
#include "nsProtectedAuthThread.h"
#include "nsContentUtils.h"
#include "nsICertOverrideService.h"
#include "nsIHttpChannelInternal.h"
#include "nsIPrompt.h"
#include "nsISupportsPriority.h"
#include "nsITokenDialogs.h"
#include "nsIUploadChannel.h"
#include "nsIPrompt.h"
#include "nsProxyRelease.h"
#include "PSMRunnable.h"
#include "nsContentUtils.h"
#include "nsIHttpChannelInternal.h"
#include "nsISupportsPriority.h"
#include "nsIWebProgressListener.h"
#include "nsNetUtil.h"
#include "nsNSSComponent.h"
#include "nsNSSIOLayer.h"
#include "nsProtectedAuthThread.h"
#include "nsProxyRelease.h"
#include "pkix/pkixtypes.h"
#include "PSMRunnable.h"
#include "SharedSSLState.h"
#include "ssl.h"
#include "sslproto.h"
@ -1235,6 +1237,17 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
bool renegotiationUnsafe = !siteSupportsSafeRenego &&
ioLayerHelpers.treatUnsafeNegotiationAsBroken();
/* Set the SSL Status information */
RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
if (!status) {
status = new nsSSLStatus();
infoObject->SetSSLStatus(status);
}
RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject,
status);
uint32_t state;
if (usesWeakCipher || renegotiationUnsafe) {
state = nsIWebProgressListener::STATE_IS_BROKEN;
@ -1252,6 +1265,39 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
infoObject->GetPort());
}
}
if (status->HasServerCert()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback KEEPING existing cert\n"));
} else {
ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd));
RefPtr<nsNSSCertificate> nssc(nsNSSCertificate::Create(serverCert.get()));
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback using NEW cert %p\n", nssc.get()));
status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown);
}
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
if (overrideService) {
bool haveOverride;
uint32_t overrideBits = 0; // Unused.
bool isTemporaryOverride; // Unused.
const nsACString& hostString(infoObject->GetHostName());
const int32_t port(infoObject->GetPort());
nsCOMPtr<nsIX509Cert> cert;
status->GetServerCert(getter_AddRefs(cert));
nsresult nsrv = overrideService->HasMatchingOverride(hostString, port,
cert,
&overrideBits,
&isTemporaryOverride,
&haveOverride);
if (NS_SUCCEEDED(nsrv) && haveOverride) {
state |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
}
}
infoObject->SetSecurityState(state);
// XXX Bug 883674: We shouldn't be formatting messages here in PSM; instead,
@ -1270,27 +1316,6 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
nsContentUtils::LogSimpleConsoleError(msg, "SSL");
}
/* Set the SSL Status information */
RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
if (!status) {
status = new nsSSLStatus();
infoObject->SetSSLStatus(status);
}
RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject,
status);
if (status->HasServerCert()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback KEEPING existing cert\n"));
} else {
ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd));
RefPtr<nsNSSCertificate> nssc(nsNSSCertificate::Create(serverCert.get()));
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback using NEW cert %p\n", nssc.get()));
status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown);
}
infoObject->NoteTimeUntilReady();
infoObject->SetHandshakeCompleted();
}

View File

@ -1117,6 +1117,14 @@ nsNSSComponent::InitializeNSS()
return NS_ERROR_FAILURE;
}
// Initialize the cert override service
nsCOMPtr<nsICertOverrideService> coService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
if (!coService) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Cannot initialize cert override service\n"));
return NS_ERROR_FAILURE;
}
if (PK11_IsFIPS()) {
Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true);
}

View File

@ -107,6 +107,7 @@ nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
, mIsViewSource(false)
, mSubRequestsBrokenSecurity(0)
, mSubRequestsNoSecurity(0)
, mCertUserOverridden(false)
, mRestoreSubrequests(false)
, mOnLocationChangeSeen(false)
#ifdef DEBUG
@ -233,6 +234,10 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat
if (ev && (*aState & STATE_IS_SECURE))
*aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
if (mCertUserOverridden && (*aState & STATE_IS_SECURE)) {
*aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
}
nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
if (!docShell)
return NS_OK;
@ -260,6 +265,9 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat
if (ev) {
*aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
}
if (mCertUserOverridden) {
*aState |= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
}
}
// * If so, the state should be broken or insecure; overriding the previous
// state set by the lock parameter.
@ -793,6 +801,11 @@ nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress* aWebProgress,
f -= nsIWebProgressListener::STATE_SECURE_HIGH;
info.AppendLiteral("SECURE_HIGH ");
}
if (f & nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN)
{
f -= nsIWebProgressListener::STATE_CERT_USER_OVERRIDDEN;
info.AppendLiteral("STATE_CERT_USER_OVERRIDDEN ");
}
if (f & nsIWebProgressListener::STATE_RESTORING)
{
f -= nsIWebProgressListener::STATE_RESTORING;
@ -1131,6 +1144,9 @@ nsSecureBrowserUIImpl::UpdateSecurityState(nsIRequest* aRequest,
newSecurityState = lis_broken_security;
}
mCertUserOverridden =
mNewToplevelSecurityState & STATE_CERT_USER_OVERRIDDEN;
MOZ_LOG(gSecureDocLog, LogLevel::Debug,
("SecureUI:%p: UpdateSecurityState: old-new %d - %d\n", this,
mNotifiedSecurityState, newSecurityState));

View File

@ -74,6 +74,7 @@ protected:
int32_t mDocumentRequestsInProgress;
int32_t mSubRequestsBrokenSecurity;
int32_t mSubRequestsNoSecurity;
bool mCertUserOverridden;
bool mRestoreSubrequests;
bool mOnLocationChangeSeen;
#ifdef DEBUG

View File

@ -662,7 +662,11 @@ function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
add_connection_test(aHost, aExpectedError, null,
add_cert_override.bind(this, aHost, aExpectedBits));
add_connection_test(aHost, PRErrorCodeSuccess);
add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => {
Assert.ok(aSecurityInfo.securityState &
Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
"Cert override flag should be set on the security state");
});
}
// Helper function for add_prevented_cert_override_test. This is much like

View File

@ -5,4 +5,8 @@ config = {
'src_mozconfig': 'mobile/android/config/mozconfigs/android-api-15-frontend/nightly',
'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-frontend/releng.manifest',
'multi_locale_config_platform': 'android',
'postflight_build_mach_commands': [
['gradle', 'app:lintAutomationDebug'],
['gradle', 'app:testAutomationDebugUnitTest'],
],
}

View File

@ -1881,6 +1881,26 @@ or run without that action (ie: --no-{action})"
if self.config.get('enable_ccache'):
self._ccache_s()
# A list of argument lists. Better names gratefully accepted!
mach_commands = self.config.get('postflight_build_mach_commands', [])
for mach_command in mach_commands:
self._execute_postflight_build_mach_command(mach_command)
def _execute_postflight_build_mach_command(self, mach_command_args):
env = self.query_build_env()
env.update(self.query_mach_build_env())
python = self.query_exe('python2.7')
command = [python, 'mach', '--log-no-times']
command.extend(mach_command_args)
self.run_command_m(
command=command,
cwd=self.query_abs_dirs()['abs_src_dir'],
env=env, output_timeout=self.config.get('max_build_output_timeout', 60 * 20),
halt_on_failure=True,
)
def preflight_package_source(self):
self._get_mozconfig()

View File

@ -39,11 +39,30 @@ task:
MH_CUSTOM_BUILD_VARIANT_CFG: api-15-frontend
MH_BRANCH: {{project}}
MH_BUILD_POOL: taskcluster
GRADLE_USER_HOME: '/home/worker/workspace/build/src/dotgradle'
maxRunTime: 36000
command: ["/bin/bash", "bin/build.sh"]
artifacts:
'public/android/unittest':
type: directory
path: '/home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/reports/tests'
expires: '{{#from_now}}1 year{{/from_now}}'
'public/android/lint/lint-results-automationDebug.html':
type: file
path: '/home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/outputs/lint-results-automationDebug.html'
expires: '{{#from_now}}1 year{{/from_now}}'
'public/android/lint/lint-results-automationDebug.xml':
type: file
path: '/home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/outputs/lint-results-automationDebug.xml'
expires: '{{#from_now}}1 year{{/from_now}}'
'public/android/lint/lint-results-automationDebug_files':
type: directory
path: '/home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/outputs/lint-results-automationDebug_files'
expires: '{{#from_now}}1 year{{/from_now}}'
extra:
treeherderEnv:
- production
@ -51,7 +70,11 @@ task:
treeherder:
machine:
# see https://github.com/mozilla/treeherder/blob/master/ui/js/values.js
platform: android-4-0-armv7-api15-frontend
platform: android-4-0-armv7-api15
groupSymbol: tc
groupName: Submitted by taskcluster
symbol: Unit
tier: 2
# Rather then enforcing particular conventions we require that all build
# tasks provide the "build" extra field to specify where the build and tests
# files are located.

View File

@ -46,6 +46,7 @@ var {
Messenger,
injectAPI,
flushJarCache,
detectLanguage,
} = ExtensionUtils;
function isWhenBeforeOrSame(when1, when2) {
@ -122,6 +123,11 @@ var api = context => {
getUILanguage: function() {
return context.extension.localeData.uiLocale;
},
detectLanguage: function(text, callback) {
let result = detectLanguage(text);
return context.wrapPromise(result, callback);
},
},
};
};

View File

@ -16,7 +16,8 @@ Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
"resource:///modules/translation/LanguageDetector.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Locale",
"resource://gre/modules/Locale.jsm");
@ -433,7 +434,6 @@ LocaleData.prototype = {
// locale code.
return Locale.getLocale().replace(/-/g, "_");
},
};
// This is a generic class for managing event listeners. Example usage:
@ -961,6 +961,18 @@ const PlatformInfo = Object.freeze({
})(),
});
function detectLanguage(text) {
return LanguageDetector.detectLanguage(text).then(result => ({
isReliable: result.confident,
languages: result.languages.map(lang => {
return {
language: lang.languageCode,
percentage: lang.percent,
};
}),
}));
}
this.ExtensionUtils = {
runSafeWithoutClone,
runSafeSyncWithoutClone,
@ -980,4 +992,5 @@ this.ExtensionUtils = {
extend,
flushJarCache,
instanceOf,
detectLanguage,
};

View File

@ -1,5 +1,10 @@
"use strict";
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
detectLanguage,
} = ExtensionUtils;
extensions.registerSchemaAPI("i18n", null, (extension, context) => {
return {
i18n: {
@ -10,6 +15,10 @@ extensions.registerSchemaAPI("i18n", null, (extension, context) => {
getUILanguage: function() {
return extension.localeData.uiLocale;
},
detectLanguage: function(text) {
return detectLanguage(text);
},
},
};
});

View File

@ -78,7 +78,6 @@
},
{
"name": "detectLanguage",
"unsupported": true,
"type": "function",
"description": "Detects the language of the provided text using CLD.",
"async": "callback",

View File

@ -242,6 +242,119 @@ add_task(function* test_get_ui_language() {
yield extension.unload();
});
add_task(function* test_detect_language() {
const af_string = " aam skukuza die naam beteken hy wat skoonvee of hy wat alles onderstebo keer wysig " +
"bosveldkampe boskampe is kleiner afgeleë ruskampe wat oor min fasiliteite beskik daar is geen restaurante " +
"of winkels nie en slegs oornagbesoekers word toegelaat bateleur";
// String with intermixed French/English text
const fr_en_string = "France is the largest country in Western Europe and the third-largest in Europe as a whole. " +
"A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter " +
"Cet article concerne le pays européen aujourdhui appelé République française. Pour dautres usages du nom France, " +
"Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus." +
"Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumped over the lazy dog";
function backgroundScript() {
function checkResult(source, result, expected) {
browser.test.assertEq(expected.isReliable, result.isReliable, "result.confident is true");
browser.test.assertEq(
expected.languages.length,
result.languages.length,
`result.languages contains the expected number of languages in ${source}`);
expected.languages.forEach((lang, index) => {
browser.test.assertEq(
lang.percentage,
result.languages[index].percentage,
`element ${index} of result.languages array has the expected percentage in ${source}`);
browser.test.assertEq(
lang.language,
result.languages[index].language,
`element ${index} of result.languages array has the expected language in ${source}`);
});
}
let tabId;
browser.tabs.query({currentWindow: true, active: true}, tabs => {
tabId = tabs[0].id;
browser.test.sendMessage("ready");
});
browser.test.onMessage.addListener(([msg, expected]) => {
Promise.all([
browser.i18n.detectLanguage(msg),
new Promise(
resolve => browser.tabs.sendMessage(tabId, msg, resolve)),
]).then(([backgroundResults, contentResults]) => {
checkResult("background", backgroundResults, expected);
checkResult("contentScript", contentResults, expected);
browser.test.sendMessage("done");
});
});
}
function content() {
browser.runtime.onMessage.addListener((msg, sender, respond) => {
browser.i18n.detectLanguage(msg, respond);
return true;
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"run_at": "document_start",
"js": ["content_script.js"],
}],
},
background: `(${backgroundScript})()`,
files: {
"content_script.js": `(${content})()`,
},
});
let win = window.open("file_sample.html");
yield extension.startup();
yield extension.awaitMessage("ready");
let expected = {
isReliable: true,
languages: [
{
language: "fr",
percentage: 67,
},
{
language: "en",
percentage: 32,
},
],
};
extension.sendMessage([fr_en_string, expected]);
yield extension.awaitMessage("done");
expected = {
isReliable: true,
languages: [
{
language: "af",
percentage: 99,
},
],
};
extension.sendMessage([af_string, expected]);
yield extension.awaitMessage("done");
win.close();
yield extension.unload();
});
</script>
</body>

View File

@ -464,7 +464,7 @@ Database::Init()
// like views, temp triggers or temp tables. The database should not be
// considered corrupt if any of the following fails.
rv = InitTempTriggers();
rv = InitTempEntities();
NS_ENSURE_SUCCESS(rv, rv);
// Notify we have finished database initialization.
@ -1015,7 +1015,7 @@ Database::InitFunctions()
}
nsresult
Database::InitTempTriggers()
Database::InitTempEntities()
{
MOZ_ASSERT(NS_IsMainThread());
@ -1027,6 +1027,10 @@ Database::InitTempTriggers()
// Add the triggers that update the moz_hosts table as necessary.
rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERINSERT_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEHOSTS_TEMP);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEHOSTS_AFTERDELETE_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER);

View File

@ -242,9 +242,9 @@ protected:
nsresult InitFunctions();
/**
* Initializes triggers defined in nsPlacesTriggers.h
* Initializes temp entities, like triggers, tables, views...
*/
nsresult InitTempTriggers();
nsresult InitTempEntities();
/**
* Helpers used by schema upgrades.

View File

@ -1884,16 +1884,29 @@ private:
}
#endif
nsCString query("DELETE FROM moz_places "
"WHERE id IN (");
query.Append(placeIdsToRemove);
query.Append(')');
{
nsCString query("DELETE FROM moz_places "
"WHERE id IN (");
query.Append(placeIdsToRemove);
query.Append(')');
nsCOMPtr<mozIStorageStatement> stmt = mHistory->GetStatement(query);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> stmt = mHistory->GetStatement(query);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
{
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
nsAutoCString query("DELETE FROM moz_updatehosts_temp");
nsCOMPtr<mozIStorageStatement> stmt = mHistory->GetStatement(query);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}

View File

@ -501,8 +501,12 @@ var removePagesById = Task.async(function*(db, idList) {
if (idList.length == 0) {
return;
}
// Note, we are already in a transaction, since callers create it.
yield db.execute(`DELETE FROM moz_places
WHERE id IN ( ${ sqlList(idList) } )`);
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
yield db.execute(`DELETE FROM moz_updatehosts_temp`);
});
/**

View File

@ -2455,6 +2455,13 @@ nsNavHistory::CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString)
);
NS_ENSURE_SUCCESS(rv, rv);
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
rv = mDB->MainConn()->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DELETE FROM moz_updatehosts_temp")
);
NS_ENSURE_SUCCESS(rv, rv);
// Invalidate frecencies of touched places, since they need recalculation.
rv = invalidateFrecencies(aPlaceIdsQueryString);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -241,6 +241,15 @@ const EXPIRATION_QUERIES = {
actions: ACTION.CLEAR_HISTORY
},
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
QUERY_UPDATE_HOSTS: {
sql: `DELETE FROM moz_updatehosts_temp`,
actions: ACTION.CLEAR_HISTORY | ACTION.TIMED | ACTION.TIMED_OVERLIMIT |
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
ACTION.DEBUG
},
// Expire orphan icons from the database.
QUERY_EXPIRE_FAVICONS: {
sql: `DELETE FROM moz_favicons WHERE id IN (

View File

@ -144,4 +144,15 @@
")" \
)
// This table is used, along with moz_places_afterdelete_trigger, to update
// hosts after places removals. During a DELETE FROM moz_places, hosts are
// accumulated into this table, then a DELETE FROM moz_updatehosts_temp will
// take care of updating the moz_hosts table for every modified host.
// See CREATE_PLACES_AFTERDELETE_TRIGGER in nsPlacestriggers.h for details.
#define CREATE_UPDATEHOSTS_TEMP NS_LITERAL_CSTRING( \
"CREATE TEMP TABLE moz_updatehosts_temp (" \
" host TEXT PRIMARY KEY " \
") WITHOUT ROWID " \
)
#endif // __nsPlacesTables_h__

View File

@ -110,12 +110,31 @@
"END" \
)
// This is a hack to workaround the lack of FOR EACH STATEMENT in Sqlite, until
// bug 871908 can be fixed properly.
// We store the modified hosts in a temp table, and after every DELETE FROM
// moz_places, we issue a DELETE FROM moz_updatehosts_temp. The AFTER DELETE
// trigger will then take care of updating the moz_hosts table.
// Note this way we lose atomicity, crashing between the 2 queries may break the
// hosts table coherency. So it's better to run those DELETE queries in a single
// transaction.
// Regardless, this is still better than hanging the browser for several minutes
// on a fast machine.
#define CREATE_PLACES_AFTERDELETE_TRIGGER NS_LITERAL_CSTRING( \
"CREATE TEMP TRIGGER moz_places_afterdelete_trigger " \
"AFTER DELETE ON moz_places FOR EACH ROW " \
"BEGIN " \
"INSERT OR IGNORE INTO moz_updatehosts_temp (host)" \
"VALUES (fixup_url(get_unreversed_host(OLD.rev_host)));" \
"END" \
)
#define CREATE_UPDATEHOSTS_AFTERDELETE_TRIGGER NS_LITERAL_CSTRING( \
"CREATE TEMP TRIGGER moz_updatehosts_afterdelete_trigger " \
"AFTER DELETE ON moz_updatehosts_temp FOR EACH ROW " \
"BEGIN " \
"DELETE FROM moz_hosts " \
"WHERE host = fixup_url(get_unreversed_host(OLD.rev_host)) " \
"WHERE host = OLD.host " \
"AND NOT EXISTS(" \
"SELECT 1 FROM moz_places " \
"WHERE rev_host = get_unreversed_host(host || '.') || '.' " \
@ -123,7 +142,7 @@
"); " \
"UPDATE moz_hosts " \
"SET prefix = (" HOSTS_PREFIX_PRIORITY_FRAGMENT ") " \
"WHERE host = fixup_url(get_unreversed_host(OLD.rev_host)); " \
"WHERE host = OLD.host; " \
"END" \
)

View File

@ -660,13 +660,15 @@ var Impl = {
// and payload is the telemetry payload from that child process.
_childTelemetry: [],
// Thread hangs from child processes.
// Used for TelemetrySession.getChildThreadHangs(); not sent with Telemetry pings.
// TelemetrySession.getChildThreadHangs() is used by extensions such as Statuser (https://github.com/chutten/statuser).
// Each element is in the format {source: <weak-ref>, payload: <object>},
// where source is a weak reference to the child process,
// and payload contains the thread hang stats from that child process.
_childThreadHangs: [],
// Array of the resolve functions of all the promises that are waiting for the child thread hang stats to arrive, used to resolve all those promises at once
// Array of the resolve functions of all the promises that are waiting for the child thread hang stats to arrive, used to resolve all those promises at once.
_childThreadHangsResolveFunctions: [],
// Timeout function for child thread hang stats retrieval
// Timeout function for child thread hang stats retrieval.
_childThreadHangsTimeout: null,
// Unique id that identifies this session so the server can cope with duplicate
// submissions, orphaning and other oddities. The id is shared across subsessions.

View File

@ -205,8 +205,6 @@ function isCorrectlySigned(aAddon) {
return true;
if (aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING)
return false;
if (aAddon.foreignInstall && aAddon.signedState < AddonManager.SIGNEDSTATE_SIGNED)
return false;
return true;
}

View File

@ -693,8 +693,6 @@ function isUsableAddon(aAddon) {
mustSign(aAddon.type)) {
if (aAddon.signedState <= AddonManager.SIGNEDSTATE_MISSING)
return false;
if (aAddon.foreignInstall && aAddon.signedState < AddonManager.SIGNEDSTATE_SIGNED)
return false;
}
if (aAddon.blocklistState == Blocklist.STATE_BLOCKED)
@ -8015,6 +8013,12 @@ Object.assign(SystemAddonInstallLocation.prototype, {
* to cleanup again next time.
*/
cleanDirectories: Task.async(function*() {
// System add-ons directory does not exist
if (!(yield OS.File.exists(this._baseDir.path))) {
return;
}
let iterator;
try {
iterator = new OS.File.DirectoryIterator(this._baseDir.path);

View File

@ -868,13 +868,11 @@ add_test(function() {
is_element_hidden(get("detail-disable-btn"), "Disable button should be hidden");
is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
is_element_hidden(get("detail-warning"), "Warning message should be hidden");
is_element_visible(get("detail-warning"), "Warning message should be visible");
is(get("detail-warning").textContent, "Test add-on 11 is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
is_element_visible(get("detail-error"), "Error message should be visible");
is(get("detail-error").textContent, "Test add-on 11 could not be verified for use in " + gApp + " and has been disabled.", "Error message should be correct");
is_element_visible(get("detail-error-link"), "Error link should be visible");
is(get("detail-error-link").value, "More Information", "Error link text should be correct");
is(get("detail-error-link").href, infoURL, "Error link should be correct");
is_element_hidden(get("detail-error"), "Error message should be hidden");
is_element_hidden(get("detail-error-link"), "Error link should be hidden");
close_manager(gManagerWindow, function() {
Services.prefs.setBoolPref("xpinstall.signatures.required", false);

View File

@ -110,8 +110,6 @@ add_task(function*() {
id: "addon12@tests.mozilla.org",
name: "Test add-on 12",
signedState: AddonManager.SIGNEDSTATE_PRELIMINARY,
isActive: false,
appDisabled: true,
foreignInstall: true,
}, {
id: "addon13@tests.mozilla.org",
@ -892,16 +890,13 @@ add_task(function*() {
is_element_hidden(get_node(addon, "preferences-btn"), "Preferences button should be hidden");
is_element_hidden(get_node(addon, "enable-btn"), "Enable button should be hidden");
is_element_hidden(get_node(addon, "disable-btn"), "Disable button should be hidden");
is_element_visible(get_node(addon, "disable-btn"), "Disable button should be visible");
is_element_visible(get_node(addon, "remove-btn"), "Remove button should be visible");
is_element_hidden(get_node(addon, "warning"), "Warning message should be hidden");
is_element_hidden(get_node(addon, "warning-link"), "Warning link should be hidden");
is_element_visible(get_node(addon, "error"), "Error message should be visible");
is(get_node(addon, "error").textContent, "Test add-on 12 could not be verified for use in " + gApp + " and has been disabled.", "Error message should be correct");
is_element_visible(get_node(addon, "error-link"), "Error link should be visible");
is(get_node(addon, "error-link").value, "More Information", "Error link text should be correct");
is(get_node(addon, "error-link").href, infoURL, "Error link should be correct");
is_element_hidden(get_node(addon, "error"), "Error message should be hidden");
is_element_hidden(get_node(addon, "error-link"), "Error link should be hidden");
info("Addon 13");
addon = items["Test add-on 13"];
@ -936,10 +931,9 @@ add_task(function*() {
is_element_visible(signingInfoUI, "Signing info UI should be visible");
items = get_test_items();
is(Object.keys(items).length, 3, "Two add-ons should be shown");
is(Object.keys(items).length, 2, "Two add-ons should be shown");
is(Object.keys(items)[0], "Test add-on 10", "The disabled unsigned extension should be shown");
is(Object.keys(items)[1], "Test add-on 11", "The disabled unsigned extension should be shown");
is(Object.keys(items)[2], "Test add-on 12", "The disabled foreign installed extension should be shown");
showAllButton.click();

View File

@ -336,7 +336,7 @@ add_task(function*() {
resetPrefs();
});
// Only fully-signed sideloaded add-ons should work
// Preliminarily-signed sideloaded add-ons should work
add_task(function*() {
let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.preliminary), profileDir, ID);
@ -345,10 +345,10 @@ add_task(function*() {
// Currently we leave the sideloaded add-on there but just don't run it
let addon = yield promiseAddonByID(ID);
do_check_neq(addon, null);
do_check_true(addon.appDisabled);
do_check_false(addon.isActive);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
do_check_eq(getActiveVersion(), -1);
do_check_eq(getActiveVersion(), 2);
addon.uninstall();
yield promiseShutdownManager();

View File

@ -262,9 +262,13 @@ interface nsIWebProgressListener : nsISupports
*
* STATE_USES_WEAK_CRYPTO
* The topmost document uses a weak cipher suite such as RC4.
*
* STATE_CERT_USER_OVERRIDDEN
* The user has added a security exception for the site.
*/
const unsigned long STATE_USES_SSL_3 = 0x01000000;
const unsigned long STATE_USES_WEAK_CRYPTO = 0x02000000;
const unsigned long STATE_CERT_USER_OVERRIDDEN = 0x04000000;
/**
* Notification indicating the state has changed for one of the requests