Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-11-28 14:29:23 +01:00
commit 09cbc383e8
71 changed files with 877 additions and 1097 deletions

View File

@ -329,15 +329,6 @@ pref("media.video-queue.default-size", 3);
// optimize images' memory usage
pref("image.mem.decodeondraw", true);
pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
pref("image.mem.min_discard_timeout_ms", 86400000); /* 24h, we rely on the out of memory hook */
// At this point 'max_decoded_image_kb' only applies to animated images. They're
// unfortunately fairly large, so this pref still needs to be somewhat generous,
// but it makes sense to reduce it since most types of images are now in the
// surface cache. Once animated images are stored in the surface cache too, this
// pref will go away; see bug 977459. The same goes for
// 'hard_limit_decoded_image_kb'; the surface cache limits are all hard.
pref("image.mem.max_decoded_image_kb", 30000);
pref("image.mem.hard_limit_decoded_image_kb", 66560);
// Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
// Almost everything that was factored into 'max_decoded_image_kb' is now stored
// in the surface cache. 1/8 of main memory is 32MB on a 256MB device, which is

View File

@ -34,20 +34,22 @@ function close() {
return p.kill();
}
let appinfo = {};
let name;
AddonManager.getAddonByID(require("addon").id, function (addon) {
appinfo.label = addon.name.replace(" Simulator", "");
name = addon.name.replace(" Simulator", "");
Simulator.register(appinfo.label, {
appinfo: appinfo,
Simulator.register(name, {
// We keep the deprecated `appinfo` object so that recent simulator addons
// remain forward-compatible with older Firefox.
appinfo: { label: name },
launch: launch,
close: close
});
});
exports.shutdown = function () {
Simulator.unregister(appinfo.label);
Simulator.unregister(name);
close();
}

View File

@ -76,6 +76,36 @@ a {
min-width: 70px;
}
#searchIcon {
border: 1px solid transparent;
-moz-margin-end: 5px;
height: 38px;
width: 38px;
background-image: url("chrome://browser/skin/magnifier.png");
background-size: 26px;
background-position: center center;
background-repeat: no-repeat;
}
#searchIcon[active],
#searchIcon:hover {
background-color: #e9e9e9;
border: 1px solid rgb(226, 227, 229);
border-radius: 2.5px;
}
html[searchUIConfiguration="oldsearchui"] #searchIcon {
display: none;
}
html:not([searchUIConfiguration="oldsearchui"]) #searchText::-moz-placeholder {
color: transparent;
}
html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
display: none;
}
#searchText {
-moz-box-flex: 1;
padding: 6px 8px;
@ -368,6 +398,10 @@ body[narrow] #restorePreviousSession::before {
background-image: url("chrome://branding/content/about-logo@2x.png");
}
#searchIcon {
background-image: url("chrome://browser/skin/magnifier@2x.png");
}
#defaultSnippet1,
#defaultSnippet2,
#rightsSnippet {

View File

@ -41,7 +41,8 @@
<div id="searchContainer">
<form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
<div id="searchLogoContainer"><img id="searchEngineLogo"/></div>
<div id="searchLogoContainer" hidden="true"><img id="searchEngineLogo"/></div>
<button id="searchIcon" type="button" />
<input type="text" name="q" value="" id="searchText" maxlength="256"
autofocus="autofocus" dir="auto"/>
<input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/>

View File

@ -1221,6 +1221,29 @@ toolbarpaletteitem[place="palette"][hidden] {
animation-duration: 2s;
}
#abouthome-search-panel .panel-arrowcontent {
-moz-padding-start: 0;
-moz-padding-end: 0;
padding-top: 0;
padding-bottom: 0;
background: rgb(248, 250, 251);
font-size: 110%;
}
.abouthome-search-panel-item {
-moz-box-align: center;
padding-top: 4px;
padding-bottom: 4px;
-moz-padding-start: 24px;
-moz-padding-end: 24px;
}
.abouthome-search-panel-item > label {
-moz-padding-start: 0;
-moz-margin-start: 0;
color: rgb(130, 132, 133);
}
/* Combined context-menu items */
#context-navigation > .menuitem-iconic > .menu-iconic-text,
#context-navigation > .menuitem-iconic > .menu-accel-container {

View File

@ -276,6 +276,14 @@
</hbox>
</panel>
<panel id="abouthome-search-panel" orient="vertical" type="arrow" hidden="true"
onclick="this.hidePopup()">
<hbox id="abouthome-search-panel-manage" class="abouthome-search-panel-item"
onclick="openPreferences('paneSearch')">
<label>&changeSearchSettings.button;</label>
</hbox>
</panel>
<panel id="social-share-panel"
class="social-panel"
type="arrow"

View File

@ -269,6 +269,9 @@ let AboutHomeListener = {
case "AboutHomeSearchEvent":
this.onSearch(aEvent);
break;
case "AboutHomeSearchPanel":
this.onOpenSearchPanel(aEvent);
break;
case "click":
this.onClick(aEvent);
break;
@ -320,6 +323,10 @@ let AboutHomeListener = {
addEventListener("click", this, true);
addEventListener("pagehide", this, true);
if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
doc.documentElement.setAttribute("searchUIConfiguration", "oldsearchui");
}
// XXX bug 738646 - when Marketplace is launched, remove this statement and
// the hidden attribute set on the apps button in aboutHome.xhtml
if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
@ -328,6 +335,7 @@ let AboutHomeListener = {
sendAsyncMessage("AboutHome:RequestUpdate");
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
doc.addEventListener("AboutHomeSearchPanel", this, true, true);
},
onClick: function(aEvent) {
@ -378,6 +386,10 @@ let AboutHomeListener = {
case "settings":
sendAsyncMessage("AboutHome:Settings");
break;
case "searchIcon":
sendAsyncMessage("AboutHome:OpenSearchPanel", null, { anchor: originalTarget });
break;
}
},
@ -397,6 +409,10 @@ let AboutHomeListener = {
sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
},
onOpenSearchPanel: function(aEvent) {
sendAsyncMessage("AboutHome:OpenSearchPanel");
},
onFocusInput: function () {
let searchInput = content.document.getElementById("searchText");
if (searchInput) {

View File

@ -345,6 +345,19 @@ input[type=button] {
background-size: 26px 26px;
}
#newtab-search-logo.magnifier {
width: 38px; /* 26 image width + 6 left "padding" + 6 right "padding" */
-moz-margin-end: 5px;
background-size: 26px;
background-image: url("chrome://browser/skin/magnifier.png");
}
@media not all and (max-resolution: 1dppx) {
#newtab-search-icon.magnifier {
background-image: url("chrome://browser/skin/magnifier@2x.png");
}
}
#newtab-search-logo[type="logo"] {
background-size: 65px 26px;
width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
@ -366,7 +379,7 @@ input[type=button] {
}
#newtab-search-text {
height: 38px;
height: 38px; /* same height as #newtab-search-logo */
-moz-box-flex: 1;
padding: 0 8px;
@ -390,7 +403,7 @@ input[type=button] {
}
#newtab-search-submit {
height: 38px;
height: 38px; /* same height as #newtab-search-logo */
font-size: 13px !important;
-moz-margin-start: -1px;

View File

@ -14,6 +14,8 @@
%newTabDTD;
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
%searchBarDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
@ -28,7 +30,7 @@
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
noautohide="true" hidden="true">
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
<xul:label>&cmd_engineManager.label;</xul:label>
<xul:label>&changeSearchSettings.button;</xul:label>
</xul:hbox>
</xul:panel>

View File

@ -8,12 +8,23 @@ let gSearch = {
currentEngineName: null,
get useNewUI() {
let newUI = Services.prefs.getBoolPref("browser.search.showOneOffButtons");
delete this.useNewUI;
this.useNewUI = newUI;
return newUI;
},
init: function () {
for (let idSuffix of this._nodeIDSuffixes) {
this._nodes[idSuffix] =
document.getElementById("newtab-search-" + idSuffix);
}
if (this.useNewUI) {
this._nodes.logo.classList.add("magnifier");
}
window.addEventListener("ContentSearchService", this);
this._send("GetState");
},
@ -122,6 +133,11 @@ let gSearch = {
},
_setUpPanel: function () {
// The new search UI only contains the "manage" engine entry in the panel
if (this.useNewUI) {
return;
}
// Build the panel if necessary.
if (this._newEngines) {
this._buildPanel(this._newEngines);
@ -198,28 +214,30 @@ let gSearch = {
_setCurrentEngine: function (engine) {
this.currentEngineName = engine.name;
let type = "";
let uri;
let logoBuf = window.devicePixelRatio >= 2 ?
engine.logo2xBuffer || engine.logoBuffer :
engine.logoBuffer || engine.logo2xBuffer;
if (logoBuf) {
uri = URL.createObjectURL(new Blob([logoBuf]));
type = "logo";
}
else if (engine.iconBuffer) {
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
type = "favicon";
}
this._nodes.logo.setAttribute("type", type);
if (!this.useNewUI) {
let type = "";
let uri;
let logoBuf = window.devicePixelRatio >= 2 ?
engine.logo2xBuffer || engine.logoBuffer :
engine.logoBuffer || engine.logo2xBuffer;
if (logoBuf) {
uri = URL.createObjectURL(new Blob([logoBuf]));
type = "logo";
}
else if (engine.iconBuffer) {
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
type = "favicon";
}
this._nodes.logo.setAttribute("type", type);
if (uri) {
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
if (uri) {
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
}
else {
this._nodes.logo.style.backgroundImage = "";
}
this._nodes.text.placeholder = engine.placeholder;
}
else {
this._nodes.logo.style.backgroundImage = "";
}
this._nodes.text.placeholder = engine.placeholder;
// Set up the suggestion controller.
if (!this._suggestionController) {

View File

@ -78,25 +78,6 @@ let gTests = [
}
},
{
desc: "Check that search engine logo has alt text",
setup: function () { },
run: function ()
{
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchEngineLogoElt = doc.getElementById("searchEngineLogo");
ok(searchEngineLogoElt, "Found search engine logo");
let altText = searchEngineLogoElt.alt;
ok(typeof altText == "string" && altText.length > 0,
"Search engine logo's alt text is a nonempty string");
isnot(altText, "undefined",
"Search engine logo's alt text shouldn't be the string 'undefined'");
}
},
// Disabled on Linux for intermittent issues with FHR, see Bug 945667.
{
desc: "Check that performing a search fires a search event and records to " +
@ -252,52 +233,6 @@ let gTests = [
}
},
{
desc: "Check that the search UI/ action is updated when the search engine is changed",
setup: function() {},
run: function()
{
let currEngine = Services.search.currentEngine;
let unusedEngines = [].concat(Services.search.getVisibleEngines()).filter(x => x != currEngine);
let searchbar = document.getElementById("searchbar");
function checkSearchUI(engine) {
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
let searchText = doc.getElementById("searchText");
let logoElt = doc.getElementById("searchEngineLogo");
let engineName = doc.documentElement.getAttribute("searchEngineName");
is(engineName, engine.name, "Engine name should've been updated");
if (!logoElt.parentNode.hidden) {
is(logoElt.alt, engineName, "Alt text of logo image should match search engine name")
} else {
is(searchText.placeholder, engineName, "Placeholder text should match search engine name");
}
}
// Do a sanity check that all attributes are correctly set to begin with
checkSearchUI(currEngine);
let deferred = Promise.defer();
promiseBrowserAttributes(gBrowser.selectedTab).then(function() {
// Test if the update propagated
checkSearchUI(unusedEngines[0]);
searchbar.currentEngine = currEngine;
deferred.resolve();
});
// The following cleanup function will set currentEngine back to the previous
// engine if we fail to do so above.
registerCleanupFunction(function() {
searchbar.currentEngine = currEngine;
});
// Set the current search engine to an unused one
searchbar.currentEngine = unusedEngines[0];
searchbar.select();
return deferred.promise;
}
},
{
desc: "Check POST search engine support",
setup: function() {},
@ -492,6 +427,26 @@ let gTests = [
is(gBrowser.currentURI.spec, "about:accounts?entrypoint=abouthome",
"Entry point should be `abouthome`.");
})
},
{
desc: "Clicking the icon should open the popup",
setup: function () {},
run: Task.async(function* () {
let doc = gBrowser.selectedBrowser.contentDocument;
let searchIcon = doc.getElementById("searchIcon");
let panel = window.document.getElementById("abouthome-search-panel");
info("Waiting for popup to open");
EventUtils.synthesizeMouseAtCenter(searchIcon, {}, gBrowser.selectedBrowser.contentWindow);
yield promiseWaitForEvent(panel, "popupshown");
ok("Saw popup open");
let promise = promisePrefsOpen();
let item = window.document.getElementById("abouthome-search-panel-manage");
EventUtils.synthesizeMouseAtCenter(item, {});
yield promise;
})
}
];
@ -667,6 +622,24 @@ function waitForLoad(cb) {
}, true);
}
function promiseWaitForEvent(node, type, capturing) {
return new Promise((resolve) => {
node.addEventListener(type, function listener(event) {
node.removeEventListener(type, listener, capturing);
resolve(event);
}, capturing);
});
}
let promisePrefsOpen = Task.async(function*() {
info("Waiting for the preferences tab to open...");
let event = yield promiseWaitForEvent(gBrowser.tabContainer, "TabOpen", true);
let tab = event.target;
yield promiseTabLoadEvent(tab);
is(tab.linkedBrowser.currentURI.spec, "about:preferences#search", "Should have seen the prefs tab");
gBrowser.removeTab(tab);
});
function promiseNewEngine(basename) {
info("Waiting for engine to be added: " + basename);
let addDeferred = Promise.defer();

View File

@ -119,41 +119,12 @@ let runTaskifiedTests = Task.async(function* () {
promiseClick(logoImg()),
]);
// In the search panel, click the no-logo engine. It should become the
// current engine.
let noLogoBox = null;
for (let box of panel.childNodes) {
if (box.getAttribute("engine") == noLogoEngine.name) {
noLogoBox = box;
break;
}
}
ok(noLogoBox, "Search panel should contain the no-logo engine");
yield Promise.all([
promiseSearchEvents(["CurrentEngine"]),
promiseClick(noLogoBox),
]);
yield checkCurrentEngine(ENGINE_NO_LOGO);
// Switch back to the 1x-and-2x logo engine.
Services.search.currentEngine = logo1x2xEngine;
yield promiseSearchEvents(["CurrentEngine"]);
yield checkCurrentEngine(ENGINE_1X_2X_LOGO);
// Open the panel again.
yield Promise.all([
promisePanelShown(panel),
promiseClick(logoImg()),
]);
// In the search panel, click the Manage Engines box.
let manageBox = $("manage");
ok(!!manageBox, "The Manage Engines box should be present in the document");
yield Promise.all([
promiseManagerOpen(),
promiseClick(manageBox),
]);
is(panel.childNodes.length, 1, "Search panel should only contain the Manage Engines entry");
is(panel.childNodes[0], manageBox, "Search panel should contain the Manage Engines entry");
panel.hidePopup();
// Add the engine that provides search suggestions and switch to it.
let suggestionEngine = yield promiseNewSearchEngine(ENGINE_SUGGESTIONS);
@ -340,42 +311,6 @@ let checkCurrentEngine = Task.async(function* ({name: basename, logoPrefix1x, lo
// gSearch.currentEngineName
is(gSearch().currentEngineName, engine.name,
"currentEngineName: " + engine.name);
let expectedLogoPrefix = window.devicePixelRatio >= 2 ? logoPrefix2x : logoPrefix1x;
// Check that the right logo is set.
let logo = logoImg();
if (expectedLogoPrefix) {
let objectURL = logo.style.backgroundImage.match(/^url\("([^"]*)"\)$/)[1];
ok(objectURL, "ObjectURL should be there.");
let blob = yield objectURLToBlob(objectURL);
let base64 = yield blobToBase64(blob);
ok(base64.startsWith(expectedLogoPrefix), "Checking image prefix.");
logo.click();
let panel = searchPanel();
yield promisePanelShown(panel);
panel.hidePopup();
for (let engineBox of panel.childNodes) {
let engineName = engineBox.getAttribute("engine");
if (engineName == engine.name) {
is(engineBox.getAttribute("selected"), "true",
"Engine box's selected attribute should be true for " +
"selected engine: " + engineName);
}
else {
ok(!engineBox.hasAttribute("selected"),
"Engine box's selected attribute should be absent for " +
"non-selected engine: " + engineName);
}
}
}
else {
is(logo.style.backgroundImage, "", "backgroundImage should be empty");
}
});
function promisePanelShown(panel) {
@ -399,31 +334,6 @@ function promiseClick(node) {
return deferred.promise;
}
function promiseManagerOpen() {
info("Waiting for the search manager window to open...");
let deferred = Promise.defer();
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onWin(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onWin);
ok(true, "Observed search manager window opened");
is(subj.opener, gWindow,
"Search engine manager opener should be the chrome browser " +
"window containing the newtab page");
subj.close();
deferred.resolve();
}
});
}
});
return deferred.promise;
}
function searchPanel() {
return $("panel");
}

View File

@ -629,6 +629,12 @@
]]></body>
</method>
<method name="inputChanged">
<body><![CDATA[
this.updateGoButtonVisibility();
]]></body>
</method>
<method name="updateGoButtonVisibility">
<body><![CDATA[
document.getAnonymousElementByAttribute(this, "anonid",
@ -657,8 +663,8 @@
</method>
</implementation>
<handlers>
<handler event="input" action="this.updateGoButtonVisibility();"/>
<handler event="drop" action="this.updateGoButtonVisibility();"/>
<handler event="input" action="this.inputChanged();"/>
<handler event="drop" action="this.inputChanged();"/>
<handler event="focus">
<![CDATA[
if (this._textbox.value)

View File

@ -137,7 +137,7 @@ let UI = {
startSimulator: function(version) {
this._portBeforeSimulatorStarted = this.connection.port;
let port = ConnectionManager.getFreeTCPPort();
let simulator = Simulator.getByVersion(version);
let simulator = Simulator.getByName(version);
if (!simulator) {
this.connection.log("Error: can't find simulator: " + version);
return;

View File

@ -9,11 +9,11 @@ const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
let store = new ObservableObject({versions:[]});
function feedStore() {
store.object.versions = Simulator.availableVersions().map(v => {
let simulator = Simulator.getByVersion(v);
store.object.versions = Simulator.availableNames().map(name => {
let simulator = Simulator.getByName(name);
return {
version: v,
label: simulator.appinfo.label
version: name,
label: simulator ? name : "Unknown"
}
});
}

View File

@ -159,11 +159,14 @@ BrowserToolboxProcess.prototype = {
try {
debuggingProfileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
} catch (ex) {
if (ex.result !== Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
// Don't re-copy over the prefs again if this profile already exists
if (ex.result === Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
this._dbgProfilePath = debuggingProfileDir.path;
} else {
dumpn("Error trying to create a profile directory, failing.");
dumpn("Error: " + (ex.message || ex));
return;
}
return;
}
this._dbgProfilePath = debuggingProfileDir.path;

View File

@ -71,9 +71,21 @@ function openToolbox(form) {
};
devtools.TargetFactory.forRemoteTab(options).then(target => {
let frame = document.getElementById("toolbox-iframe");
let selectedTool = "jsdebugger";
try {
// Remember the last panel that was used inside of this profile.
selectedTool = Services.prefs.getCharPref("devtools.toolbox.selectedTool");
} catch(e) {}
try {
// But if we are testing, then it should always open the debugger panel.
selectedTool = Services.prefs.getCharPref("devtools.browsertoolbox.panel");
} catch(e) {}
let options = { customIframe: frame };
gDevTools.showToolbox(target,
"jsdebugger",
selectedTool,
devtools.Toolbox.HostType.CUSTOM,
options)
.then(onNewToolbox);

View File

@ -204,7 +204,7 @@ var TextEditor = Class({
*/
load: function(resource) {
// Wait for the editor.appendTo and resource.load before proceeding.
// They can run in parallel.
// They can run in parallel.
return promise.all([
resource.load(),
this.appended
@ -245,7 +245,9 @@ var TextEditor = Class({
*/
focus: function() {
return this.appended.then(() => {
this.editor.focus();
if (this.editor) {
this.editor.focus();
}
});
}
});

View File

@ -6,8 +6,6 @@ const {Cu} = require("chrome");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm");
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const {GetAddonsJSON} = require("devtools/webide/remote-resources");

View File

@ -205,8 +205,8 @@ let SimulatorScanner = {
_updateRuntimes() {
this._runtimes = [];
for (let version of Simulator.availableVersions()) {
this._runtimes.push(new SimulatorRuntime(version));
for (let name of Simulator.availableNames()) {
this._runtimes.push(new SimulatorRuntime(name));
}
this._emitUpdated();
},
@ -463,15 +463,15 @@ WiFiRuntime.prototype = {
// For testing use only
exports._WiFiRuntime = WiFiRuntime;
function SimulatorRuntime(version) {
this.version = version;
function SimulatorRuntime(name) {
this.name = name;
}
SimulatorRuntime.prototype = {
type: RuntimeTypes.SIMULATOR,
connect: function(connection) {
let port = ConnectionManager.getFreeTCPPort();
let simulator = Simulator.getByVersion(this.version);
let simulator = Simulator.getByName(this.name);
if (!simulator || !simulator.launch) {
return promise.reject("Can't find simulator: " + this.name);
}
@ -484,14 +484,7 @@ SimulatorRuntime.prototype = {
});
},
get id() {
return this.version;
},
get name() {
let simulator = Simulator.getByVersion(this.version);
if (!simulator) {
return "Unknown";
}
return Simulator.getByVersion(this.version).appinfo.label;
return this.name;
},
};

View File

@ -28,10 +28,14 @@
adbAddonsInstalled.resolve();
});
function onSimulatorInstalled(version) {
function getVersion(name) {
return name.match(/(\d+\.\d+)/)[0];
}
function onSimulatorInstalled(name) {
let deferred = promise.defer();
Simulator.on("register", function onUpdate() {
if (Simulator.getByVersion(version)) {
if (Simulator.getByName(name)) {
Simulator.off("register", onUpdate);
nextTick().then(deferred.resolve);
}
@ -39,17 +43,17 @@
return deferred.promise;
}
function installSimulatorFromUI(doc, version) {
let li = doc.querySelector('[addon="simulator-' + version + '"]');
function installSimulatorFromUI(doc, name) {
let li = doc.querySelector('[addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".install-button").click();
return onSimulatorInstalled(version);
return onSimulatorInstalled(name);
}
function uninstallSimulatorFromUI(doc, version) {
function uninstallSimulatorFromUI(doc, name) {
let deferred = promise.defer();
Simulator.on("unregister", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + version + '"]');
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + getVersion(name) + '"]');
if (li) {
Simulator.off("unregister", onUpdate);
deferred.resolve();
@ -58,7 +62,7 @@
}
})
});
let li = doc.querySelector('[status="installed"][addon="simulator-' + version + '"]');
let li = doc.querySelector('[status="installed"][addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".uninstall-button").click();
return deferred.promise;
}
@ -100,7 +104,7 @@
let sim10 = addons.simulators.filter(a => a.version == "1.0")[0];
sim10.install();
yield onSimulatorInstalled("1.0");
yield onSimulatorInstalled("Firefox OS 1.0");
win.Cmds.showAddons();
@ -119,11 +123,11 @@
info("Uninstalling Simulator 2.0");
yield installSimulatorFromUI(addonDoc, "2.0");
yield installSimulatorFromUI(addonDoc, "Firefox OS 2.0");
info("Uninstalling Simulator 3.0");
yield installSimulatorFromUI(addonDoc, "3.0");
yield installSimulatorFromUI(addonDoc, "Firefox OS 3.0");
yield nextTick();
@ -136,9 +140,9 @@
items = panelNode.querySelectorAll(".runtime-panel-item-simulator");
is(items.length, 3, "Found 3 simulators button");
yield uninstallSimulatorFromUI(addonDoc, "1.0");
yield uninstallSimulatorFromUI(addonDoc, "2.0");
yield uninstallSimulatorFromUI(addonDoc, "3.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 1.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 2.0");
yield uninstallSimulatorFromUI(addonDoc, "Firefox OS 3.0");
items = panelNode.querySelectorAll(".runtime-panel-item-simulator");
is(items.length, 0, "No simulator listed");

View File

@ -170,6 +170,7 @@
@RESPATH@/components/browser-element.xpt
@RESPATH@/browser/components/browsercompsbase.xpt
@RESPATH@/browser/components/browser-feeds.xpt
@RESPATH@/browser/components/browsermodules.manifest
@RESPATH@/components/caps.xpt
@RESPATH@/components/chrome.xpt
@RESPATH@/components/commandhandler.xpt

View File

@ -97,6 +97,7 @@ let AboutHome = {
"AboutHome:Settings",
"AboutHome:RequestUpdate",
"AboutHome:Search",
"AboutHome:OpenSearchPanel",
],
init: function() {
@ -203,6 +204,18 @@ let AboutHome = {
});
break;
case "AboutHome:OpenSearchPanel":
let panel = window.document.getElementById("abouthome-search-panel");
let anchor = aMessage.objects.anchor;
panel.hidden = false;
panel.openPopup(anchor);
anchor.setAttribute("active", "true");
panel.addEventListener("popuphidden", function onHidden() {
panel.removeEventListener("popuphidden", onHidden);
anchor.removeAttribute("active");
});
break;
}
},

View File

@ -220,6 +220,12 @@ this.ContentSearch = {
_onMessageManageEngines: function (msg, data) {
let browserWin = msg.target.ownerDocument.defaultView;
if (Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
browserWin.openPreferences("paneSearch");
return Promise.resolve();
}
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator);
let window = wm.getMostRecentWindow("Browser:SearchManager");

View File

@ -4,7 +4,7 @@
"use strict";
this.EXPORTED_SYMBOLS = ["UITour"];
this.EXPORTED_SYMBOLS = ["UITour", "UITourMetricsProvider"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
@ -23,7 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
"resource://gre/modules/UITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Metrics",
"resource://gre/modules/Metrics.jsm");
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL = "browser.uitour.loglevel";
@ -521,6 +522,64 @@ this.UITour = {
}).catch(log.error);
break;
}
case "setDefaultSearchEngine": {
let enginePromise = this.selectSearchEngine(data.identifier);
enginePromise.catch(Cu.reportError);
break;
}
case "setTreatmentTag": {
let name = data.name;
let value = data.value;
let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
string.data = value;
Services.prefs.setComplexValue("browser.uitour.treatment." + name,
Ci.nsISupportsString, string);
UITourHealthReport.recordTreatmentTag(name, value);
break;
}
case "getTreatmentTag": {
let name = data.name;
let value;
try {
value = Services.prefs.getComplexValue("browser.uitour.treatment." + name,
Ci.nsISupportsString).data;
} catch (ex) {}
this.sendPageCallback(messageManager, data.callbackID, { value: value });
break;
}
case "setSearchTerm": {
let targetPromise = this.getTarget(window, "search");
targetPromise.then(target => {
let searchbar = target.node;
searchbar.value = data.term;
searchbar.inputChanged();
}).then(null, Cu.reportError);
break;
}
case "openSearchPanel": {
let targetPromise = this.getTarget(window, "search");
targetPromise.then(target => {
let searchbar = target.node;
if (searchbar.textbox.open) {
this.sendPageCallback(messageManager, data.callbackID);
} else {
let onPopupShown = () => {
searchbar.textbox.popup.removeEventListener("popupshown", onPopupShown);
this.sendPageCallback(messageManager, data.callbackID);
};
searchbar.textbox.popup.addEventListener("popupshown", onPopupShown);
searchbar.openSuggestionsPanel();
}
}).then(null, Cu.reportError);
break;
}
}
if (!window.gMultiProcessBrowser) { // Non-e10s. See bug 1089000.
@ -1105,9 +1164,14 @@ this.UITour = {
tooltip.setAttribute("targetName", aAnchor.targetName);
tooltip.hidden = false;
let xOffset = 0, yOffset = 0;
let alignment = "bottomcenter topright";
if (aAnchor.targetName == "search") {
alignment = "after_start";
xOffset = 18;
}
this._addAnnotationPanelMutationObserver(tooltip);
tooltip.openPopup(aAnchorEl, alignment);
tooltip.openPopup(aAnchorEl, alignment, xOffset, yOffset);
if (tooltip.state == "closed") {
document.defaultView.addEventListener("endmodalstate", function endModalStateHandler() {
document.defaultView.removeEventListener("endmodalstate", endModalStateHandler);
@ -1290,6 +1354,19 @@ this.UITour = {
props.forEach(property => appinfo[property] = Services.appinfo[property]);
this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
break;
case "selectedSearchEngine":
Services.search.init(rv => {
let engine;
if (Components.isSuccessCode(rv)) {
engine = Services.search.defaultEngine;
} else {
engine = { identifier: "" };
}
this.sendPageCallback(aMessageManager, aCallbackID, {
searchEngineIdentifier: engine.identifier
});
});
break;
default:
log.error("getConfiguration: Unknown configuration requested: " + aConfiguration);
break;
@ -1397,6 +1474,26 @@ this.UITour = {
}
},
selectSearchEngine(aID) {
return new Promise((resolve, reject) => {
Services.search.init((rv) => {
if (!Components.isSuccessCode(rv)) {
reject("selectSearchEngine: search service init failed: " + rv);
return;
}
let engines = Services.search.getVisibleEngines();
for (let engine of engines) {
if (engine.identifier == aID) {
Services.search.defaultEngine = engine;
return resolve();
}
}
reject("selectSearchEngine could not find engine with given ID");
});
});
},
getAvailableSearchEngineTargets(aWindow) {
return new Promise(resolve => {
this.getTarget(aWindow, "search").then(searchTarget => {
@ -1448,3 +1545,105 @@ this.UITour = {
};
this.UITour.init();
/**
* UITour Health Report
*/
const DAILY_DISCRETE_TEXT_FIELD = Metrics.Storage.FIELD_DAILY_DISCRETE_TEXT;
/**
* Public API to be called by the UITour code
*/
const UITourHealthReport = {
recordTreatmentTag: function(tag, value) {
#ifdef MOZ_SERVICES_HEALTHREPORT
Task.spawn(function*() {
let reporter = Cc["@mozilla.org/datareporting/service;1"]
.getService()
.wrappedJSObject
.healthReporter;
// This can happen if the FHR component of the data reporting service is
// disabled. This is controlled by a pref that most will never use.
if (!reporter) {
return;
}
yield reporter.onInit();
// Get the UITourMetricsProvider instance from the Health Reporter
reporter.getProvider("org.mozilla.uitour").recordTreatmentTag(tag, value);
});
#endif
}
};
this.UITourMetricsProvider = function() {
Metrics.Provider.call(this);
}
UITourMetricsProvider.prototype = Object.freeze({
__proto__: Metrics.Provider.prototype,
name: "org.mozilla.uitour",
measurementTypes: [
UITourTreatmentMeasurement1,
],
recordTreatmentTag: function(tag, value) {
let m = this.getMeasurement(UITourTreatmentMeasurement1.prototype.name,
UITourTreatmentMeasurement1.prototype.version);
let field = tag;
if (this.storage.hasFieldFromMeasurement(m.id, field,
DAILY_DISCRETE_TEXT_FIELD)) {
let fieldID = this.storage.fieldIDFromMeasurement(m.id, field);
return this.enqueueStorageOperation(function recordKnownField() {
return this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
}.bind(this));
}
// Otherwise, we first need to create the field.
return this.enqueueStorageOperation(function recordField() {
// This function has to return a promise.
return Task.spawn(function () {
let fieldID = yield this.storage.registerField(m.id, field,
DAILY_DISCRETE_TEXT_FIELD);
yield this.storage.addDailyDiscreteTextFromFieldID(fieldID, value);
}.bind(this));
}.bind(this));
},
});
function UITourTreatmentMeasurement1() {
Metrics.Measurement.call(this);
this._serializers = {};
this._serializers[this.SERIALIZE_JSON] = {
//singular: We don't need a singular serializer because we have none of this data
daily: this._serializeJSONDaily.bind(this)
};
}
UITourTreatmentMeasurement1.prototype = Object.freeze({
__proto__: Metrics.Measurement.prototype,
name: "treatment",
version: 1,
// our fields are dynamic
fields: { },
// We need a custom serializer because the default one doesn't accept unknown fields
_serializeJSONDaily: function(data) {
let result = {_v: this.version };
for (let [field, data] of data) {
result[field] = data;
}
return result;
}
});

View File

@ -0,0 +1,3 @@
#ifdef MOZ_SERVICES_HEALTHREPORT
category healthreport-js-provider-default UITourMetricsProvider resource:///modules/UITour.jsm
#endif

View File

@ -55,5 +55,9 @@ EXTRA_PP_JS_MODULES += [
'webrtcUI.jsm',
]
EXTRA_PP_COMPONENTS += [
'browsermodules.manifest',
]
if CONFIG['MOZILLA_OFFICIAL']:
DEFINES['MOZILLA_OFFICIAL'] = 1

View File

@ -70,34 +70,6 @@ add_task(function* SetCurrentEngine() {
});
});
add_task(function* ManageEngines() {
yield addTab();
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "ManageEngines",
});
let deferred = Promise.defer();
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onOpen(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onOpen);
ok(true, "Observed search manager window open");
is(subj.opener, window,
"Search engine manager opener should be this chrome window");
subj.close();
deferred.resolve();
}
});
}
});
info("Waiting for search engine manager window to open...");
yield deferred.promise;
});
add_task(function* modifyEngine() {
yield addTab();
let engine = Services.search.currentEngine;

View File

@ -369,6 +369,44 @@ let tests = [
});
});
},
function test_select_search_engine(done) {
Services.search.init(rv => {
if (!Components.isSuccessCode(rv)) {
ok(false, "search service init failed: " + rv);
done();
return;
}
let defaultEngine = Services.search.defaultEngine;
gContentAPI.getConfiguration("availableTargets", data => {
let searchEngines = data.targets.filter(t => t.startsWith("searchEngine-"));
let someOtherEngineID = searchEngines.filter(t => t != "searchEngine-" + defaultEngine.identifier)[0];
someOtherEngineID = someOtherEngineID.replace(/^searchEngine-/, "");
let observe = function (subject, topic, verb) {
info("browser-search-engine-modified: " + verb);
if (verb == "engine-current") {
is(Services.search.defaultEngine.identifier, someOtherEngineID, "correct engine was switched to");
done();
}
};
Services.obs.addObserver(observe, "browser-search-engine-modified", false);
registerCleanupFunction(() => {
// Clean up
Services.obs.removeObserver(observe, "browser-search-engine-modified");
Services.search.defaultEngine = defaultEngine;
});
gContentAPI.setDefaultSearchEngine(someOtherEngineID);
});
});
},
function test_treatment_tag(done) {
gContentAPI.setTreatmentTag("foobar", "baz");
gContentAPI.getTreatmentTag("foobar", (data) => {
is(data.value, "baz", "set and retrieved treatmentTag");
done();
});
},
// Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down.
taskify(function* cleanupMenus() {

View File

@ -8,6 +8,7 @@ let gContentAPI;
let gContentWindow;
Components.utils.import("resource:///modules/UITour.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
requestLongerTimeout(2);
@ -145,4 +146,54 @@ let tests = [
popup.removeAttribute("animate");
}),
function test_getConfiguration_selectedSearchEngine(done) {
Services.search.init(rv => {
ok(Components.isSuccessCode(rv), "Search service initialized");
let engine = Services.search.defaultEngine;
gContentAPI.getConfiguration("selectedSearchEngine", (data) => {
is(data.searchEngineIdentifier, engine.identifier, "Correct engine identifier");
done();
});
});
},
function test_setSearchTerm(done) {
const TERM = "UITour Search Term";
gContentAPI.setSearchTerm(TERM);
let searchbar = document.getElementById("searchbar");
// The UITour gets to the searchbar element through a promise, so the value setting
// only happens after a tick.
waitForCondition(() => searchbar.value == TERM, done, "Correct term set");
},
function test_clearSearchTerm(done) {
gContentAPI.setSearchTerm("");
let searchbar = document.getElementById("searchbar");
// The UITour gets to the searchbar element through a promise, so the value setting
// only happens after a tick.
waitForCondition(() => searchbar.value == "", done, "Search term cleared");
},
function test_openSearchPanel(done) {
let searchbar = document.getElementById("searchbar");
// If suggestions are enabled, the panel will attempt to use the network to connect
// to the suggestions provider, causing the test suite to fail.
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.search.suggest.enabled");
});
ok(!searchbar.textbox.open, "Popup starts as closed");
gContentAPI.openSearchPanel(() => {
ok(searchbar.textbox.open, "Popup was opened");
searchbar.textbox.closePopup();
ok(!searchbar.textbox.open, "Popup was closed");
done();
});
},
];

View File

@ -2,7 +2,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource:///modules/UITour.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const SINGLE_TRY_TIMEOUT = 100;
@ -219,6 +220,7 @@ function UITourTest() {
registerCleanupFunction(function() {
delete window.UITour;
delete window.UITourMetricsProvider;
delete window.gContentWindow;
delete window.gContentAPI;
if (gTestTab)

View File

@ -207,4 +207,36 @@ if (typeof Mozilla == 'undefined') {
});
};
Mozilla.UITour.setDefaultSearchEngine = function(identifier) {
_sendEvent('setDefaultSearchEngine', {
identifier: identifier,
});
};
Mozilla.UITour.setTreatmentTag = function(name, value) {
_sendEvent('setTreatmentTag', {
name: name,
value: value
});
};
Mozilla.UITour.getTreatmentTag = function(name, callback) {
_sendEvent('getTreatmentTag', {
name: name,
callbackID: _waitForCallback(callback)
});
};
Mozilla.UITour.setSearchTerm = function(term) {
_sendEvent('setSearchTerm', {
term: term
});
};
Mozilla.UITour.openSearchPanel = function(callback) {
_sendEvent('openSearchPanel', {
callbackID: _waitForCallback(callback)
});
};
})();

View File

@ -40,6 +40,8 @@ browser.jar:
skin/classic/browser/identity-icons-https-mixed-active.png
skin/classic/browser/identity-icons-https-mixed-display.png
skin/classic/browser/Info.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menuPanel.png

View File

@ -58,6 +58,8 @@ browser.jar:
skin/classic/browser/notification-16@2x.png
skin/classic/browser/notification-64.png
skin/classic/browser/notification-64@2x.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menuPanel.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

View File

@ -45,6 +45,8 @@ browser.jar:
skin/classic/browser/keyhole-forward-mask.svg
skin/classic/browser/KUI-background.png
skin/classic/browser/livemark-folder.png
skin/classic/browser/magnifier.png (../shared/magnifier.png)
skin/classic/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/browser/mask.png (../shared/mask.png)
skin/classic/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/browser/menu-back.png
@ -483,6 +485,8 @@ browser.jar:
skin/classic/aero/browser/keyhole-forward-mask.svg
skin/classic/aero/browser/KUI-background.png
skin/classic/aero/browser/livemark-folder.png (livemark-folder-aero.png)
skin/classic/aero/browser/magnifier.png (../shared/magnifier.png)
skin/classic/aero/browser/magnifier@2x.png (../shared/magnifier@2x.png)
skin/classic/aero/browser/mask.png (../shared/mask.png)
skin/classic/aero/browser/mask@2x.png (../shared/mask@2x.png)
skin/classic/aero/browser/menu-back.png (menu-back-aero.png)

View File

@ -460,14 +460,31 @@ JS::Value
WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname)
{
switch (pname) {
case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
case LOCAL_GL_TEXTURE_BASE_LEVEL:
case LOCAL_GL_TEXTURE_COMPARE_FUNC:
case LOCAL_GL_TEXTURE_COMPARE_MODE:
case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT:
case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS:
case LOCAL_GL_TEXTURE_MAX_LEVEL:
case LOCAL_GL_TEXTURE_SWIZZLE_A:
case LOCAL_GL_TEXTURE_SWIZZLE_B:
case LOCAL_GL_TEXTURE_SWIZZLE_G:
case LOCAL_GL_TEXTURE_SWIZZLE_R:
case LOCAL_GL_TEXTURE_WRAP_R:
{
GLint i = 0;
gl->fGetTexParameteriv(target.get(), pname, &i);
return JS::NumberValue(uint32_t(i));
}
case LOCAL_GL_TEXTURE_MAX_LOD:
case LOCAL_GL_TEXTURE_MIN_LOD:
{
GLfloat f = 0.0f;
gl->fGetTexParameterfv(target.get(), pname, &f);
return JS::NumberValue(float(f));
}
}
return WebGLContext::GetTexParameterInternal(target, pname);
}

View File

@ -1579,6 +1579,35 @@ void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname,
else
tex->SetMaxMipmapLevel(intParam);
break;
case LOCAL_GL_TEXTURE_COMPARE_MODE:
if (!IsWebGL2())
return ErrorInvalidEnumInfo("texParameter: pname", pname);
paramValueInvalid = (intParam != LOCAL_GL_NONE &&
intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE);
break;
case LOCAL_GL_TEXTURE_COMPARE_FUNC:
if (!IsWebGL2())
return ErrorInvalidEnumInfo("texParameter: pname", pname);
switch (intParam) {
case LOCAL_GL_LEQUAL:
case LOCAL_GL_GEQUAL:
case LOCAL_GL_LESS:
case LOCAL_GL_GREATER:
case LOCAL_GL_EQUAL:
case LOCAL_GL_NOTEQUAL:
case LOCAL_GL_ALWAYS:
case LOCAL_GL_NEVER:
paramValueInvalid = false;
default:
paramValueInvalid = true;
}
break;
case LOCAL_GL_TEXTURE_MIN_FILTER:
switch (intParam) {
case LOCAL_GL_NEAREST:

View File

@ -1545,20 +1545,38 @@ IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
return true; // GLfloat(0.0) sets a bool to false.
case LOCAL_GL_INT:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_VEC2:
case LOCAL_GL_INT_VEC3:
case LOCAL_GL_INT_VEC4:
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
return setterType == LOCAL_GL_INT;
case LOCAL_GL_FLOAT:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT3:
case LOCAL_GL_FLOAT_MAT3x2:
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4:
case LOCAL_GL_FLOAT_MAT4x2:
case LOCAL_GL_FLOAT_MAT4x3:
case LOCAL_GL_FLOAT_VEC2:
case LOCAL_GL_FLOAT_VEC3:
case LOCAL_GL_FLOAT_VEC4:
case LOCAL_GL_FLOAT_MAT2:
case LOCAL_GL_FLOAT_MAT3:
case LOCAL_GL_FLOAT_MAT4:
return setterType == LOCAL_GL_FLOAT;
default:

View File

@ -28,17 +28,17 @@ struct WebGLUniformInfo {
case LOCAL_GL_SAMPLER_2D:
case LOCAL_GL_SAMPLER_3D:
case LOCAL_GL_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
case LOCAL_GL_SAMPLER_2D_SHADOW:
case LOCAL_GL_SAMPLER_2D_ARRAY:
case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
case LOCAL_GL_SAMPLER_CUBE_SHADOW:
case LOCAL_GL_INT_SAMPLER_2D:
case LOCAL_GL_INT_SAMPLER_3D:
case LOCAL_GL_INT_SAMPLER_CUBE:
case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
return 1;
case LOCAL_GL_FLOAT_VEC2:
@ -58,16 +58,16 @@ struct WebGLUniformInfo {
case LOCAL_GL_FLOAT_MAT2:
return 4;
case LOCAL_GL_FLOAT_MAT2x3:
case LOCAL_GL_FLOAT_MAT3x2:
case LOCAL_GL_FLOAT_MAT3x2:
return 6;
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT4x2:
return 8;
case LOCAL_GL_FLOAT_MAT2x4:
case LOCAL_GL_FLOAT_MAT4x2:
return 8;
case LOCAL_GL_FLOAT_MAT3:
return 9;
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4x3:
return 12;
case LOCAL_GL_FLOAT_MAT3x4:
case LOCAL_GL_FLOAT_MAT4x3:
return 12;
case LOCAL_GL_FLOAT_MAT4:
return 16;
default:

View File

@ -236,8 +236,6 @@ private:
DECL_GFX_PREF(Live, "image.mem.decode_bytes_at_a_time", ImageMemDecodeBytesAtATime, uint32_t, 200000);
DECL_GFX_PREF(Live, "image.mem.decodeondraw", ImageMemDecodeOnDraw, bool, false);
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
DECL_GFX_PREF(Live, "image.mem.hard_limit_decoded_image_kb", ImageMemHardLimitDecodedImageKB, uint32_t, 0);
DECL_GFX_PREF(Live, "image.mem.max_decoded_image_kb", ImageMemMaxDecodedImageKB, uint32_t, 50*1024);
DECL_GFX_PREF(Live, "image.mem.max_ms_before_yield", ImageMemMaxMSBeforeYield, uint32_t, 400);
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);

View File

@ -19,7 +19,6 @@
#include "imgRequest.h"
#include "imgRequestProxy.h"
#include "imgTools.h"
#include "DiscardTracker.h"
#include "nsICOEncoder.h"
#include "nsPNGEncoder.h"
@ -92,7 +91,6 @@ mozilla::image::InitModule()
gfxPrefs::GetSingleton();
mozilla::image::ShutdownTracker::Initialize();
mozilla::image::DiscardTracker::Initialize();
mozilla::image::ImageFactory::Initialize();
mozilla::image::RasterImage::Initialize();
mozilla::image::SurfaceCache::Initialize();
@ -109,7 +107,6 @@ mozilla::image::ShutdownModule()
}
imgLoader::Shutdown();
mozilla::image::SurfaceCache::Shutdown();
mozilla::image::DiscardTracker::Shutdown();
sInitialized = false;
}

View File

@ -1,328 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsComponentManagerUtils.h"
#include "nsITimer.h"
#include "RasterImage.h"
#include "DiscardTracker.h"
#include "mozilla/Preferences.h"
#include "gfxPrefs.h"
namespace mozilla {
namespace image {
static const char* sDiscardTimeoutPref = "image.mem.min_discard_timeout_ms";
/* static */ LinkedList<DiscardTracker::Node> DiscardTracker::sDiscardableImages;
/* static */ nsCOMPtr<nsITimer> DiscardTracker::sTimer;
/* static */ bool DiscardTracker::sInitialized = false;
/* static */ bool DiscardTracker::sTimerOn = false;
/* static */ Atomic<bool> DiscardTracker::sDiscardRunnablePending(false);
/* static */ uint64_t DiscardTracker::sCurrentDecodedImageBytes = 0;
/* static */ uint32_t DiscardTracker::sMinDiscardTimeoutMs = 10000;
/* static */ PRLock * DiscardTracker::sAllocationLock = nullptr;
/* static */ Mutex* DiscardTracker::sNodeListMutex = nullptr;
/* static */ Atomic<bool> DiscardTracker::sShutdown(false);
/*
* When we notice we're using too much memory for decoded images, we enqueue a
* DiscardRunnable, which runs this code.
*/
NS_IMETHODIMP
DiscardTracker::DiscardRunnable::Run()
{
sDiscardRunnablePending = false;
DiscardTracker::DiscardNow();
return NS_OK;
}
void
DiscardTimeoutChangedCallback(const char* aPref, void *aClosure)
{
DiscardTracker::ReloadTimeout();
}
nsresult
DiscardTracker::Reset(Node *node)
{
// We shouldn't call Reset() with a null |img| pointer, on images which can't
// be discarded, or on animated images (which should be marked as
// non-discardable, anyway).
MutexAutoLock lock(*sNodeListMutex);
MOZ_ASSERT(sInitialized);
MOZ_ASSERT(node->img);
MOZ_ASSERT(node->img->CanDiscard());
MOZ_ASSERT(!node->img->mAnim);
// Insert the node at the front of the list and note when it was inserted.
bool wasInList = node->isInList();
if (wasInList) {
node->remove();
}
node->timestamp = TimeStamp::Now();
sDiscardableImages.insertFront(node);
// If the node wasn't already in the list of discardable images, then we may
// need to discard some images to stay under gfxPrefs::ImageMemMaxDecodedImageKB() limit.
// Call MaybeDiscardSoon to do this check.
if (!wasInList) {
MaybeDiscardSoon();
}
// Make sure the timer is running.
nsresult rv = EnableTimer();
NS_ENSURE_SUCCESS(rv,rv);
return NS_OK;
}
void
DiscardTracker::Remove(Node *node)
{
if (sShutdown) {
// Already shutdown. List should be empty, so just return.
return;
}
MutexAutoLock lock(*sNodeListMutex);
if (node->isInList())
node->remove();
if (sDiscardableImages.isEmpty())
DisableTimer();
}
/**
* Shut down the tracker, deallocating the timer.
*/
void
DiscardTracker::Shutdown()
{
sShutdown = true;
if (sTimer) {
sTimer->Cancel();
sTimer = nullptr;
}
// Clear the sDiscardableImages linked list so that its destructor
// (LinkedList.h) finds an empty array, which is required after bug 803688.
DiscardAll();
delete sNodeListMutex;
sNodeListMutex = nullptr;
}
/*
* Discard all the images we're tracking.
*/
void
DiscardTracker::DiscardAll()
{
MutexAutoLock lock(*sNodeListMutex);
if (!sInitialized)
return;
// Be careful: Calling Discard() on an image might cause it to be removed
// from the list!
Node *n;
while ((n = sDiscardableImages.popFirst())) {
n->img->Discard();
}
// The list is empty, so there's no need to leave the timer on.
DisableTimer();
}
/* static */ bool
DiscardTracker::TryAllocation(uint64_t aBytes)
{
MOZ_ASSERT(sInitialized);
PR_Lock(sAllocationLock);
bool enoughSpace =
!gfxPrefs::ImageMemHardLimitDecodedImageKB() ||
(gfxPrefs::ImageMemHardLimitDecodedImageKB() * 1024) - sCurrentDecodedImageBytes >= aBytes;
if (enoughSpace) {
sCurrentDecodedImageBytes += aBytes;
}
PR_Unlock(sAllocationLock);
// If we're using too much memory for decoded images, MaybeDiscardSoon will
// enqueue a callback to discard some images.
MaybeDiscardSoon();
return enoughSpace;
}
/* static */ void
DiscardTracker::InformDeallocation(uint64_t aBytes)
{
// This function is called back e.g. from RasterImage::Discard(); be careful!
MOZ_ASSERT(sInitialized);
PR_Lock(sAllocationLock);
MOZ_ASSERT(aBytes <= sCurrentDecodedImageBytes);
sCurrentDecodedImageBytes -= aBytes;
PR_Unlock(sAllocationLock);
}
/**
* Initialize the tracker.
*/
nsresult
DiscardTracker::Initialize()
{
// Watch the timeout pref for changes.
Preferences::RegisterCallback(DiscardTimeoutChangedCallback,
sDiscardTimeoutPref);
// Create the timer.
sTimer = do_CreateInstance("@mozilla.org/timer;1");
// Create a lock for safegarding the 64-bit sCurrentDecodedImageBytes
sAllocationLock = PR_NewLock();
// Create a lock for the node list.
MOZ_ASSERT(!sNodeListMutex);
sNodeListMutex = new Mutex("image::DiscardTracker");
// Mark us as initialized
sInitialized = true;
// Read the timeout pref and start the timer.
ReloadTimeout();
return NS_OK;
}
/**
* Read the discard timeout from about:config.
*/
void
DiscardTracker::ReloadTimeout()
{
// Read the timeout pref.
int32_t discardTimeout;
nsresult rv = Preferences::GetInt(sDiscardTimeoutPref, &discardTimeout);
// If we got something bogus, return.
if (!NS_SUCCEEDED(rv) || discardTimeout <= 0)
return;
// If the value didn't change, return.
if ((uint32_t) discardTimeout == sMinDiscardTimeoutMs)
return;
// Update the value.
sMinDiscardTimeoutMs = (uint32_t) discardTimeout;
// Restart the timer so the new timeout takes effect.
DisableTimer();
EnableTimer();
}
/**
* Enables the timer. No-op if the timer is already running.
*/
nsresult
DiscardTracker::EnableTimer()
{
// Nothing to do if the timer's already on or we haven't yet been
// initialized. !sTimer probably means we've shut down, so just ignore that,
// too.
if (sTimerOn || !sInitialized || !sTimer)
return NS_OK;
sTimerOn = true;
// Activate the timer. Have it call us back in (sMinDiscardTimeoutMs / 2)
// ms, so that an image is discarded between sMinDiscardTimeoutMs and
// (3/2 * sMinDiscardTimeoutMs) ms after it's unlocked.
return sTimer->InitWithFuncCallback(TimerCallback,
nullptr,
sMinDiscardTimeoutMs / 2,
nsITimer::TYPE_REPEATING_SLACK);
}
/*
* Disables the timer. No-op if the timer isn't running.
*/
void
DiscardTracker::DisableTimer()
{
// Nothing to do if the timer's already off.
if (!sTimerOn || !sTimer)
return;
sTimerOn = false;
// Deactivate
sTimer->Cancel();
}
/**
* Routine activated when the timer fires. This discards everything that's
* older than sMinDiscardTimeoutMs, and tries to discard enough images so that
* we go under gfxPrefs::ImageMemMaxDecodedImageKB().
*/
void
DiscardTracker::TimerCallback(nsITimer *aTimer, void *aClosure)
{
DiscardNow();
}
void
DiscardTracker::DiscardNow()
{
// Assuming the list is ordered with oldest discard tracker nodes at the back
// and newest ones at the front, iterate from back to front discarding nodes
// until we encounter one which is new enough to keep and until we go under
// our gfxPrefs::ImageMemMaxDecodedImageKB() limit.
TimeStamp now = TimeStamp::Now();
Node* node;
while ((node = sDiscardableImages.getLast())) {
if ((now - node->timestamp).ToMilliseconds() > sMinDiscardTimeoutMs ||
sCurrentDecodedImageBytes > gfxPrefs::ImageMemMaxDecodedImageKB() * 1024) {
// Discarding the image should cause sCurrentDecodedImageBytes to
// decrease via a call to InformDeallocation().
node->img->Discard();
// Careful: Discarding may have caused the node to have been removed
// from the list.
Remove(node);
}
else {
break;
}
}
// If the list is empty, disable the timer.
if (sDiscardableImages.isEmpty())
DisableTimer();
}
void
DiscardTracker::MaybeDiscardSoon()
{
// Are we carrying around too much decoded image data? If so, enqueue an
// event to try to get us down under our limit.
if (sCurrentDecodedImageBytes > gfxPrefs::ImageMemMaxDecodedImageKB() * 1024 &&
!sDiscardableImages.isEmpty()) {
// Check if the value of sDiscardRunnablePending used to be false
if (!sDiscardRunnablePending.exchange(true)) {
nsRefPtr<DiscardRunnable> runnable = new DiscardRunnable();
NS_DispatchToMainThread(runnable);
}
}
}
} // namespace image
} // namespace mozilla

View File

@ -1,140 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef mozilla_imagelib_DiscardTracker_h_
#define mozilla_imagelib_DiscardTracker_h_
#include "mozilla/Atomics.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "prlock.h"
#include "nsThreadUtils.h"
#include "nsAutoPtr.h"
class nsITimer;
namespace mozilla {
namespace image {
class RasterImage;
/**
* This static class maintains a linked list of RasterImage objects which are
* eligible for discarding.
*
* When Reset() is called, the node is removed from its position in the list
* (if it was there before) and appended to the beginnings of the list.
*
* Periodically (on a timer and when we notice that we're using more memory
* than we'd like for decoded images), we go through the list and discard
* decoded data from images at the end of the list.
*/
class DiscardTracker
{
public:
/**
* The DiscardTracker keeps a linked list of Node objects. Each object
* points to a RasterImage and contains a timestamp indicating when the
* node was inserted into the tracker.
*
* This structure is embedded within each RasterImage object, and we do
* |mDiscardTrackerNode.img = this| on RasterImage construction. Thus, a
* RasterImage must always call DiscardTracker::Remove() in its destructor
* to avoid having the tracker point to bogus memory.
*/
struct Node : public LinkedListElement<Node>
{
RasterImage *img;
TimeStamp timestamp;
};
/**
* Add an image to the front of the tracker's list, or move it to the front
* if it's already in the list. This function is main thread only.
*/
static nsresult Reset(struct Node* node);
/**
* Remove a node from the tracker; do nothing if the node is currently
* untracked. This function is main thread only.
*/
static void Remove(struct Node* node);
/**
* Initializes the discard tracker. This function is main thread only.
*/
static nsresult Initialize();
/**
* Shut the discard tracker down. This should be called on XPCOM shutdown
* so we destroy the discard timer's nsITimer. This function is main thread
* only.
*/
static void Shutdown();
/**
* Discard the decoded image data for all images tracked by the discard
* tracker. This function is main thread only.
*/
static void DiscardAll();
/**
* Inform the discard tracker that we are going to allocate some memory
* for a decoded image. We use this to determine when we've allocated
* too much memory and should discard some images. This function can be
* called from any thread and is thread-safe. If this function succeeds, the
* caller is now responsible for ensuring that InformDeallocation is called.
*/
static bool TryAllocation(uint64_t aBytes);
/**
* Inform the discard tracker that we've deallocated some memory for a
* decoded image. This function can be called from any thread and is
* thread-safe.
*/
static void InformDeallocation(uint64_t aBytes);
private:
/**
* This is called when the discard timer fires; it calls into DiscardNow().
*/
friend void DiscardTimeoutChangedCallback(const char* aPref, void *aClosure);
/**
* When run, this runnable sets sDiscardRunnablePending to false and calls
* DiscardNow().
*/
class DiscardRunnable : public nsRunnable
{
NS_IMETHOD Run();
};
static void ReloadTimeout();
static nsresult EnableTimer();
static void DisableTimer();
static void MaybeDiscardSoon();
static void TimerCallback(nsITimer *aTimer, void *aClosure);
static void DiscardNow();
static LinkedList<Node> sDiscardableImages;
static nsCOMPtr<nsITimer> sTimer;
static bool sInitialized;
static bool sTimerOn;
static Atomic<bool> sDiscardRunnablePending;
static uint64_t sCurrentDecodedImageBytes;
static uint32_t sMinDiscardTimeoutMs;
static uint32_t sMaxDecodedImageKB;
static uint32_t sHardLimitDecodedImageKB;
// Lock for safegarding the 64-bit sCurrentDecodedImageBytes
static PRLock *sAllocationLock;
static Mutex* sNodeListMutex;
static Atomic<bool> sShutdown;
};
} // namespace image
} // namespace mozilla
#endif /* mozilla_imagelib_DiscardTracker_h_ */

View File

@ -98,6 +98,10 @@ DynamicImage::OnNewSourceData()
return NS_OK;
}
void
DynamicImage::OnSurfaceDiscarded()
{ }
void
DynamicImage::SetInnerWindowID(uint64_t aInnerWindowId)
{ }

View File

@ -58,6 +58,8 @@ public:
bool aLastPart) MOZ_OVERRIDE;
virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
virtual void SetInnerWindowID(uint64_t aInnerWindowId) MOZ_OVERRIDE;
virtual uint64_t InnerWindowID() const MOZ_OVERRIDE;

View File

@ -127,6 +127,12 @@ public:
*/
virtual nsresult OnNewSourceData() = 0;
/**
* Called when the SurfaceCache discards a persistent surface belonging to
* this image.
*/
virtual void OnSurfaceDiscarded() = 0;
virtual void SetInnerWindowID(uint64_t aInnerWindowId) = 0;
virtual uint64_t InnerWindowID() const = 0;
@ -156,6 +162,8 @@ public:
virtual uint32_t GetAnimationConsumers() MOZ_OVERRIDE { return mAnimationConsumers; }
#endif
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE { }
virtual void SetInnerWindowID(uint64_t aInnerWindowId) MOZ_OVERRIDE {
mInnerWindowId = aInnerWindowId;
}

View File

@ -102,6 +102,12 @@ ImageWrapper::OnNewSourceData()
return mInnerImage->OnNewSourceData();
}
void
ImageWrapper::OnSurfaceDiscarded()
{
return mInnerImage->OnSurfaceDiscarded();
}
void
ImageWrapper::SetInnerWindowID(uint64_t aInnerWindowId)
{

View File

@ -47,6 +47,8 @@ public:
bool aLastPart) MOZ_OVERRIDE;
virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
virtual void SetInnerWindowID(uint64_t aInnerWindowId) MOZ_OVERRIDE;
virtual uint64_t InnerWindowID() const MOZ_OVERRIDE;

View File

@ -136,22 +136,6 @@ static int num_discardable_containers;
static int64_t total_source_bytes;
static int64_t discardable_source_bytes;
/* Are we globally disabling image discarding? */
static bool
DiscardingEnabled()
{
static bool inited;
static bool enabled;
if (!inited) {
inited = true;
enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nullptr);
}
return enabled;
}
class ScaleRunner : public nsRunnable
{
enum ScaleState
@ -328,8 +312,6 @@ RasterImage::RasterImage(ProgressTracker* aProgressTracker,
{
mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker);
// Set up the discard tracker node.
mDiscardTrackerNode.img = this;
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
// Statistics
@ -371,10 +353,6 @@ RasterImage::~RasterImage()
// Total statistics
num_containers--;
total_source_bytes -= mSourceData.Length();
if (NS_IsMainThread()) {
DiscardTracker::Remove(&mDiscardTrackerNode);
}
}
/* static */ void
@ -600,17 +578,26 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
if (!ref) {
// The OS threw this frame away. We need to discard and redecode.
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
if (CanForciblyDiscardAndRedecode()) {
Discard(/* aForce = */ true, /* aNotify = */ false);
ApplyDecodeFlags(aFlags);
WantDecodedFrames(aFlags, aShouldSyncNotify);
if (!ref && IsOpaque()) {
// We can use non-premultiplied alpha frames when premultipled alpha is
// requested, or vice versa, if this image is opaque. Try again with the bit
// toggled.
ref = LookupFrameInternal(aFrameNum, aSize,
aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
}
// See if we managed to redecode enough to get the frame we want.
ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
}
if (!ref) {
// The OS threw this frame away. We need to redecode if we can.
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
// Update our state so the decoder knows what to do.
mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
mDecoded = false;
mHasFirstFrame = false;
WantDecodedFrames(aFlags, aShouldSyncNotify);
// See if we managed to redecode enough to get the frame we want.
ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
if (!ref) {
// We didn't successfully redecode, so just fail.
@ -706,6 +693,14 @@ RasterImage::FrameRect(uint32_t aWhichFrame)
return nsIntRect();
}
void
RasterImage::OnSurfaceDiscarded()
{
if (mProgressTracker) {
mProgressTracker->OnDiscard();
}
}
uint32_t
RasterImage::GetNumFrames() const
{
@ -769,9 +764,6 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
if (mError)
return nullptr;
if (!ApplyDecodeFlags(aFlags))
return nullptr;
// Get the frame. If it's not there, it's probably the caller's fault for
// not waiting for the data to be loaded from the network or not passing
// FLAG_SYNC_DECODE
@ -846,9 +838,6 @@ RasterImage::GetFrameInternal(uint32_t aWhichFrame,
if (mError)
return nullptr;
if (!ApplyDecodeFlags(aFlags))
return nullptr;
// Get the frame. If it's not there, it's probably the caller's fault for
// not waiting for the data to be loaded from the network or not passing
// FLAG_SYNC_DECODE
@ -940,7 +929,7 @@ RasterImage::GetImageContainer(LayerManager* aManager, ImageContainer **_retval)
NS_ADDREF(*_retval);
// We only need to be careful about holding on to the image when it is
// discardable by the OS.
if (CanForciblyDiscardAndRedecode()) {
if (CanDiscard()) {
mImageContainerCache = mImageContainer;
mImageContainer = nullptr;
}
@ -1099,38 +1088,6 @@ RasterImage::InternalAddFrame(uint32_t aFrameNum,
return ref;
}
bool
RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
{
if (mFrameDecodeFlags == (aNewFlags & DECODE_FLAGS_MASK))
return true; // Not asking very much of us here.
if (mDecoded) {
// If the requested frame is opaque and the current and new decode flags
// only differ in the premultiply alpha bit then we can use the existing
// frame, we don't need to discard and re-decode.
uint32_t currentNonAlphaFlags =
(mFrameDecodeFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
uint32_t newNonAlphaFlags =
(aNewFlags & DECODE_FLAGS_MASK) & ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
if (currentNonAlphaFlags == newNonAlphaFlags && IsOpaque()) {
return true;
}
// if we can't discard, then we're screwed; we have no way
// to re-decode. Similarly if we aren't allowed to do a sync
// decode.
if (!(aNewFlags & FLAG_SYNC_DECODE))
return false;
if (!CanForciblyDiscardAndRedecode())
return false;
ForceDiscard();
}
mFrameDecodeFlags = aNewFlags & DECODE_FLAGS_MASK;
return true;
}
nsresult
RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
{
@ -1237,13 +1194,6 @@ RasterImage::DecodingComplete(imgFrame* aFinalFrame)
mDecoded = true;
mHasBeenDecoded = true;
// We now have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
NS_ABORT_IF_FALSE(!DiscardingActive(),
"We shouldn't have been discardable before this");
DiscardTracker::Reset(&mDiscardTrackerNode);
}
bool singleFrame = GetNumFrames() == 1;
// If there's only 1 frame, mark it as optimizable. Optimizing animated images
@ -1572,11 +1522,6 @@ RasterImage::DoImageDataComplete()
mSourceData.Length()));
}
// We now have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
return NS_OK;
}
@ -1737,12 +1682,11 @@ RasterImage::GetKeys(uint32_t *count, char ***keys)
}
void
RasterImage::Discard(bool aForce, bool aNotify)
RasterImage::Discard()
{
MOZ_ASSERT(NS_IsMainThread());
// We should be ok for discard
NS_ABORT_IF_FALSE(aForce ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
// We should never discard when we have an active decoder
NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
@ -1766,16 +1710,12 @@ RasterImage::Discard(bool aForce, bool aNotify)
mHasFirstFrame = false;
// Notify that we discarded
if (aNotify && mProgressTracker) {
if (mProgressTracker) {
mProgressTracker->OnDiscard();
}
mDecodeStatus = DecodeStatus::INACTIVE;
if (aForce) {
DiscardTracker::Remove(&mDiscardTrackerNode);
}
// Log
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
("CompressedImageAccounting: discarded uncompressed image "
@ -1792,35 +1732,13 @@ RasterImage::Discard(bool aForce, bool aNotify)
discardable_source_bytes));
}
// Helper method to determine if we can discard an image
bool
RasterImage::CanDiscard() {
return (DiscardingEnabled() && // Globally enabled...
mDiscardable && // ...Enabled at creation time...
(mLockCount == 0) && // ...not temporarily disabled...
mHasSourceData && // ...have the source data...
mDecoded); // ...and have something to discard.
}
bool
RasterImage::CanForciblyDiscard() {
return mHasSourceData; // ...have the source data...
}
bool
RasterImage::CanForciblyDiscardAndRedecode() {
return mHasSourceData && // ...have the source data...
!mDecoder && // Can't discard with an open decoder
!mAnim; // Can never discard animated images
}
// Helper method to tell us whether the clock is currently running for
// discarding this image. Mainly for assertions.
bool
RasterImage::DiscardingActive() {
return mDiscardTrackerNode.isInList();
}
// Helper method to determine if we're storing the source data in a buffer
// or just writing it directly to the decoder
bool
@ -1840,9 +1758,6 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
// We shouldn't be firing up a decoder if we already have the frames decoded
NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
// Since we're not decoded, we should not have a discard timer active
NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
// Make sure we actually get size before doing a full decode.
if (!aDoSizeDecode) {
NS_ABORT_IF_FALSE(mHasSize, "Must do a size decode before a full decode!");
@ -1995,16 +1910,6 @@ RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy
nsresult
RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
{
nsresult rv;
// If we can discard, the clock should be running. Reset it.
if (CanDiscard()) {
NS_ABORT_IF_FALSE(DiscardingActive(),
"Decoded and discardable but discarding not activated!");
rv = DiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
// Request a decode, which does nothing if we're already decoded.
if (aShouldSyncNotify) {
// We can sync notify, which means we can also sync decode.
@ -2226,13 +2131,9 @@ RasterImage::SyncDecode()
nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
CONTAINER_ENSURE_SUCCESS(rv);
if (mDecoded) {
// If we've finished decoding we need to discard so we can re-decode
// with the new flags. If we can't discard then there isn't
// anything we can do.
if (!CanForciblyDiscardAndRedecode())
return NS_ERROR_NOT_AVAILABLE;
ForceDiscard();
if (mDecoded && mAnim) {
// We can't redecode animated images, so we'll have to give up.
return NS_ERROR_NOT_AVAILABLE;
}
}
@ -2443,33 +2344,6 @@ RasterImage::Draw(gfxContext* aContext,
NS_ENSURE_ARG_POINTER(aContext);
// We can only draw without discarding and redecoding in these cases:
// * We have the default decode flags.
// * We have exactly FLAG_DECODE_NO_PREMULTIPLY_ALPHA and the current frame
// is opaque.
bool haveDefaultFlags = (mFrameDecodeFlags == DECODE_FLAGS_DEFAULT);
bool haveSafeAlphaFlags =
(mFrameDecodeFlags == FLAG_DECODE_NO_PREMULTIPLY_ALPHA) && IsOpaque();
if (!(haveDefaultFlags || haveSafeAlphaFlags)) {
if (!CanForciblyDiscardAndRedecode())
return NS_ERROR_NOT_AVAILABLE;
ForceDiscard();
mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
}
// If this image is a candidate for discarding, reset its position in the
// discard tracker so we're less likely to discard it right away.
//
// (We don't normally draw unlocked images, so this conditition will usually
// be false. But we will draw unlocked images if image locking is globally
// disabled via the image.mem.allow_locking_in_content_processes pref.)
if (DiscardingActive()) {
DiscardTracker::Reset(&mDiscardTrackerNode);
}
if (IsUnlocked() && mProgressTracker) {
mProgressTracker->OnUnlockedDraw();
}
@ -2520,9 +2394,6 @@ RasterImage::LockImage()
if (mError)
return NS_ERROR_FAILURE;
// Cancel the discard timer if it's there
DiscardTracker::Remove(&mDiscardTrackerNode);
// Increment the lock count
mLockCount++;
@ -2550,9 +2421,6 @@ RasterImage::UnlockImage()
if (mLockCount == 0)
return NS_ERROR_ABORT;
// We're locked, so discarding should not be active
NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
// Decrement our lock count
mLockCount--;
@ -2565,24 +2433,15 @@ RasterImage::UnlockImage()
// and our lock count is now zero (so nothing is forcing us to keep the
// decoded data around), try to cancel the decode and throw away whatever
// we've decoded.
if (mHasBeenDecoded && mDecoder &&
mLockCount == 0 && CanForciblyDiscard()) {
if (mHasBeenDecoded && mDecoder && mLockCount == 0 && !mAnim) {
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
("RasterImage[0x%p] canceling decode because image "
"is now unlocked.", this));
ReentrantMonitorAutoEnter lock(mDecodingMonitor);
FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
ForceDiscard();
return NS_OK;
}
// Otherwise, we might still be a candidate for discarding in the future. If
// we are, add ourselves to the discard tracker.
if (CanDiscard()) {
nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
return NS_OK;
}
@ -2591,8 +2450,11 @@ RasterImage::UnlockImage()
NS_IMETHODIMP
RasterImage::RequestDiscard()
{
if (CanDiscard() && CanForciblyDiscardAndRedecode()) {
ForceDiscard();
if (mDiscardable && // Enabled at creation time...
mLockCount == 0 && // ...not temporarily disabled...
mDecoded && // ...and have something to discard.
CanDiscard()) {
Discard();
}
return NS_OK;

View File

@ -26,7 +26,6 @@
#include "imgFrame.h"
#include "nsThreadUtils.h"
#include "DecodePool.h"
#include "DiscardTracker.h"
#include "Orientation.h"
#include "nsIObserver.h"
#include "mozilla/Maybe.h"
@ -161,6 +160,7 @@ public:
nsresult Init(const char* aMimeType,
uint32_t aFlags);
virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
virtual void OnSurfaceDiscarded() MOZ_OVERRIDE;
// Raster-specific methods
static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
@ -176,8 +176,7 @@ public:
MallocSizeOf aMallocSizeOf) const;
/* Triggers discarding. */
void Discard(bool aForce = false, bool aNotify = true);
void ForceDiscard() { Discard(/* aForce = */ true); }
void Discard();
/* Callbacks for decoders */
/** Sets the size and inherent orientation of the container. This should only
@ -319,8 +318,6 @@ private:
imgFrame* aPreviousFrame);
nsresult DoImageDataComplete();
bool ApplyDecodeFlags(uint32_t aNewFlags);
already_AddRefed<layers::Image> GetCurrentImage();
void UpdateImageContainer();
@ -364,15 +361,12 @@ private: // data
// we maybe decoding on draw).
UniquePtr<FrameAnimator> mAnim;
// Discard members
// Image locking.
uint32_t mLockCount;
DiscardTracker::Node mDiscardTrackerNode;
// Source data members
nsCString mSourceDataMimeType;
friend class DiscardTracker;
// How many times we've decoded this image.
// This is currently only used for statistics
int32_t mDecodeCount;
@ -497,9 +491,6 @@ private: // data
// Helpers
bool CanDiscard();
bool CanForciblyDiscard();
bool CanForciblyDiscardAndRedecode();
bool DiscardingActive();
bool StoringSourceData() const;
protected:

View File

@ -21,6 +21,7 @@
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "imgFrame.h"
#include "Image.h"
#include "nsAutoPtr.h"
#include "nsExpirationTracker.h"
#include "nsHashKeys.h"
@ -351,11 +352,16 @@ public:
void Remove(CachedSurface* aSurface)
{
MOZ_ASSERT(aSurface, "Should have a surface");
const ImageKey imageKey = aSurface->GetImageKey();
ImageKey imageKey = aSurface->GetImageKey();
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
MOZ_ASSERT(cache, "Shouldn't try to remove a surface with no image cache");
// If the surface was persistent, tell its image that we discarded it.
if (aSurface->GetLifetime() == Lifetime::Persistent) {
static_cast<Image*>(imageKey)->OnSurfaceDiscarded();
}
StopTracking(aSurface);
cache->Remove(aSurface);

View File

@ -6,7 +6,6 @@
#include "imgFrame.h"
#include "ImageRegion.h"
#include "DiscardTracker.h"
#include "ShutdownTracker.h"
#include "prenv.h"
@ -20,6 +19,7 @@ static bool gDisableOptimize = false;
#include "GeckoProfiler.h"
#include "mozilla/Likely.h"
#include "MainThreadUtils.h"
#include "mozilla/MemoryReporting.h"
#include "nsMargin.h"
#include "mozilla/CheckedInt.h"
@ -140,8 +140,7 @@ imgFrame::imgFrame() :
mCompositingFailed(false),
mHasNoAlpha(false),
mNonPremult(false),
mOptimizable(false),
mInformedDiscardTracker(false)
mOptimizable(false)
{
static bool hasCheckedOptimize = false;
if (!hasCheckedOptimize) {
@ -156,10 +155,6 @@ imgFrame::~imgFrame()
{
moz_free(mPalettedImageData);
mPalettedImageData = nullptr;
if (mInformedDiscardTracker) {
DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
}
}
nsresult
@ -198,13 +193,6 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
} else {
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
// Inform the discard tracker that we are going to allocate some memory.
mInformedDiscardTracker =
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
if (!mInformedDiscardTracker) {
NS_WARNING("Exceeded the image decode size hard limit");
return NS_ERROR_OUT_OF_MEMORY;
}
mVBuf = AllocateBufferForImage(mSize, mFormat);
if (!mVBuf) {
return NS_ERROR_OUT_OF_MEMORY;
@ -246,14 +234,6 @@ imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
mFormat = aFormat;
mPaletteDepth = 0;
// Inform the discard tracker that we are going to allocate some memory.
mInformedDiscardTracker =
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
if (!mInformedDiscardTracker) {
NS_WARNING("Exceed the image decode size hard limit");
return NS_ERROR_OUT_OF_MEMORY;
}
RefPtr<DrawTarget> target;
bool canUseDataSurface =
@ -368,13 +348,6 @@ nsresult imgFrame::Optimize()
mImageSurface = nullptr;
mOptSurface = nullptr;
// We just dumped most of our allocated memory, so tell the discard
// tracker that we're not using any at all.
if (mInformedDiscardTracker) {
DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
mInformedDiscardTracker = false;
}
return NS_OK;
}
}

View File

@ -207,9 +207,6 @@ private: // data
bool mNonPremult;
bool mOptimizable;
/** Have we called DiscardTracker::InformAllocation()? */
bool mInformedDiscardTracker;
friend class DrawableFrameRef;
friend class RawAccessFrameRef;
};

View File

@ -36,7 +36,6 @@
#include "nsIMemoryReporter.h"
#include "Image.h"
#include "DiscardTracker.h"
#include "gfxPrefs.h"
// we want to explore making the document own the load group
@ -955,27 +954,6 @@ nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup
return NS_OK;
}
class imgCacheObserver MOZ_FINAL : public nsIObserver
{
~imgCacheObserver() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
NS_IMPL_ISUPPORTS(imgCacheObserver, nsIObserver)
NS_IMETHODIMP
imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData)
{
if (strcmp(aTopic, "memory-pressure") == 0 ||
strcmp(aTopic, "app-theme-changed") == 0) {
DiscardTracker::DiscardAll();
}
return NS_OK;
}
class imgCacheExpirationTracker MOZ_FINAL
: public nsExpirationTracker<imgCacheEntry, 3>
{
@ -1016,8 +994,6 @@ void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
entry->Loader()->VerifyCacheSizes();
}
imgCacheObserver *gCacheObserver;
double imgLoader::sCacheTimeWeight;
uint32_t imgLoader::sCacheMaxSize;
imgMemoryReporter* imgLoader::sMemReporter;
@ -1138,15 +1114,6 @@ imgCacheQueue & imgLoader::GetCacheQueue(ImageURL *aURI)
void imgLoader::GlobalInit()
{
gCacheObserver = new imgCacheObserver();
NS_ADDREF(gCacheObserver);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->AddObserver(gCacheObserver, "memory-pressure", false);
os->AddObserver(gCacheObserver, "app-theme-changed", false);
}
sCacheTimeWeight = gfxPrefs::ImageCacheTimeWeight() / 1000.0;
int32_t cachesize = gfxPrefs::ImageCacheSize();
sCacheMaxSize = cachesize > 0 ? cachesize : 0;
@ -1284,7 +1251,6 @@ void imgLoader::Shutdown()
{
NS_IF_RELEASE(gSingleton);
NS_IF_RELEASE(gPBSingleton);
NS_RELEASE(gCacheObserver);
}
nsresult imgLoader::ClearChromeImageCache()

View File

@ -18,7 +18,6 @@ UNIFIED_SOURCES += [
'ClippedImage.cpp',
'DecodePool.cpp',
'Decoder.cpp',
'DiscardTracker.cpp',
'DynamicImage.cpp',
'FrameAnimator.cpp',
'FrameBlender.cpp',

View File

@ -19,25 +19,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=399925
<script class="testbody" type="text/javascript">
/** Test for Bug 399925. **/
var triggerDiscardingManually = false;
var pngResults = new Array();
SimpleTest.waitForExplicitFinish();
window.onload = function() {
// 1. Enable Discarding
// 2. Sets the discard timer to 500ms so we don't have to wait so long. The
// actual time is nondeterministic, but is bounded by 2 * timer = 1 second.
// It'd be nice to reduce the discard timer here, but unfortunately we only
// read that pref on startup. We instead manually trigger discarding on
// platforms where the discard timer is too long (which we'll somewhat
// arbitrarily define as 'longer than 60 seconds').
var expirationMs =
SpecialPowers.getIntPref('image.mem.surfacecache.min_expiration_ms');
if (expirationMs > 60000) {
ok(true, 'Triggering discarding manually because SurfaceCache expiration ' +
'is ' + expirationMs + ' ms');
triggerDiscardingManually = true;
} else {
ok(true, 'Using normal discarding because SurfaceCache expiration ' +
'is ' + expirationMs + ' ms');
}
// Enable discarding for the test.
SpecialPowers.pushPrefEnv({
'set':[['image.mem.discardable',true],
['image.mem.min_discard_timeout_ms',1000]]
'set':[['image.mem.discardable',true]]
}, runTest);
}
function runTest() {
// Create the image _after_ setting the discard timer pref
var image = new Image();
image.setAttribute("id", "gif");
image.src = "bug399925.gif";
document.getElementById("content").appendChild(image);
// 1. Draw the canvas once on loadComplete
// 2. Redraw the canvas and compare the results right on discard
@ -46,6 +56,16 @@ function runTest() {
is(pngResults[0], pngResults[1], "got different rendered results");
SimpleTest.finish();
});
image.src = "bug399925.gif";
document.getElementById("content").appendChild(image);
if (triggerDiscardingManually) {
var request = SpecialPowers.wrap(image)
.QueryInterface(SpecialPowers.Ci.nsIImageLoadingContent)
.getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
setTimeout(() => request.requestDiscard(), 1000);
}
}
function addCallbacks(anImage, loadCompleteCallback, discardCallback) {

View File

@ -574,7 +574,6 @@ pref("media.fragmented-mp4.android-media-codec.preferred", true);
// optimize images memory usage
pref("image.mem.decodeondraw", true);
pref("image.mem.min_discard_timeout_ms", 10000);
#ifdef NIGHTLY_BUILD
// Shumway component (SWF player) is disabled by default. Also see bug 904346.

View File

@ -3775,29 +3775,12 @@ pref("image.mem.decodeondraw", true);
// Allows image locking of decoded image data in content processes.
pref("image.mem.allow_locking_in_content_processes", true);
// Minimum timeout for image discarding (in milliseconds). The actual time in
// which an image must inactive for it to be discarded will vary between this
// value and twice this value.
//
// This used to be 120 seconds, but having it that high causes our working
// set to grow very large. Switching it back to 10 seconds will hopefully
// be better.
pref("image.mem.min_discard_timeout_ms", 10000);
// Chunk size for calls to the image decoders
pref("image.mem.decode_bytes_at_a_time", 16384);
// The longest time we can spend in an iteration of an async decode
pref("image.mem.max_ms_before_yield", 5);
// The maximum amount of decoded image data we'll willingly keep around (we
// might keep around more than this, but we'll try to get down to this value).
pref("image.mem.max_decoded_image_kb", 51200);
// Hard limit for the amount of decoded image data, 0 means we don't have the
// hard limit for it.
pref("image.mem.hard_limit_decoded_image_kb", 0);
// Minimum timeout for expiring unused images from the surface cache, in
// milliseconds. This controls how long we store cached temporary surfaces.
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms

View File

@ -1 +1 @@
NSS_3_18_BETA3
NSS_3_18_BETA4

View File

@ -10,3 +10,4 @@
*/
#error "Do not include this header file."

View File

@ -1062,9 +1062,3 @@ PK11_PrivDecrypt;
;+ local:
;+ *;
;+};
;+NSS_3.18 { # NSS 3.18 release
;+ global:
PK11_SetCertificateNickname;
;+ local:
;+ *;
;+};

View File

@ -2686,14 +2686,3 @@ PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg)
nssCryptokiObjectArray_Destroy(instances);
return slotList;
}
SECStatus
PK11_SetCertificateNickname(CERTCertificate *cert, const char *nickname)
{
/* Can't set nickname of temp cert. */
if (!cert->slot || cert->pkcs11ID == CK_INVALID_HANDLE) {
return SEC_ERROR_INVALID_ARGS;
}
return PK11_SetObjectNickname(cert->slot, cert->pkcs11ID, nickname);
}

View File

@ -458,8 +458,6 @@ SECStatus PK11_SetPrivateKeyNickname(SECKEYPrivateKey *privKey,
const char *nickname);
SECStatus PK11_SetPublicKeyNickname(SECKEYPublicKey *pubKey,
const char *nickname);
SECStatus PK11_SetCertificateNickname(CERTCertificate *cert,
const char *nickname);
/* size to hold key in bytes */
unsigned int PK11_GetKeyLength(PK11SymKey *key);

View File

@ -1831,3 +1831,32 @@ Example
"lastActiveBranch": "control"
}
org.mozilla.uitour.treatment
----------------------------
Daily measurement reporting information about treatment tagging done
by the UITour module.
Version 1
^^^^^^^^^
Daily text values in the following properties:
<tag>:
Array of discrete strings corresponding to calls for setTreatmentTag(tag, value).
Example
^^^^^^^
::
"org.mozilla.uitour.treatment": {
"_v": 1,
"treatment": [
"optin",
"optin-DNT"
],
"another-tag": [
"foobar-value"
]
}

View File

@ -23,6 +23,7 @@ user_pref("shell.checkDefaultClient", false);
user_pref("browser.warnOnQuit", false);
user_pref("accessibility.typeaheadfind.autostart", false);
user_pref("javascript.options.showInConsole", true);
user_pref("devtools.browsertoolbox.panel", "jsdebugger");
user_pref("devtools.errorconsole.enabled", true);
user_pref("devtools.debugger.remote-port", 6023);
user_pref("layout.debug.enable_data_xbl", true);

View File

@ -3905,16 +3905,7 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
if (IsQueryURI(url)) {
// Special case "place:" URIs: turn them into containers.
nsRefPtr<nsNavHistoryResultNode> resultNode;
rv = QueryRowToResult(itemId, url, title, accessCount, time, favicon,
getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv,rv);
if (itemId != -1) {
rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
resultNode->mBookmarkGuid);
NS_ENSURE_SUCCESS(rv, rv);
// We should never expose the history title for query nodes if the
// bookmark-item's title is set to null (the history title may be the
// query string without the place: prefix). Thus we call getItemTitle
@ -3923,7 +3914,18 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
rv = bookmarks->GetItemTitle(itemId, resultNode->mTitle);
rv = bookmarks->GetItemTitle(itemId, title);
NS_ENSURE_SUCCESS(rv, rv);
}
nsRefPtr<nsNavHistoryResultNode> resultNode;
rv = QueryRowToResult(itemId, url, title, accessCount, time, favicon,
getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv,rv);
if (itemId != -1) {
rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
resultNode->mBookmarkGuid);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -0,0 +1,24 @@
// Test that result node for folder shortcuts get the target folder title if
// the shortcut itself has no title set.
add_task(function* () {
let shortcutInfo = yield PlacesUtils.bookmarks.insert({
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "place:folder=TOOLBAR"
});
let unfiledRoot =
PlacesUtils.getFolderContents(PlacesUtils.unfiledBookmarksFolderId).root;
let shortcutNode = unfiledRoot.getChild(unfiledRoot.childCount - 1);
Assert.equal(shortcutNode.bookmarkGuid, shortcutInfo.guid);
let toolbarInfo =
yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.toolbarGuid);
Assert.equal(shortcutNode.title, toolbarInfo.title);
unfiledRoot.containerOpen = false;
});
function run_test() {
run_next_test();
}

View File

@ -56,6 +56,7 @@ skip-if = os == "android"
[test_486978_sort_by_date_queries.js]
[test_536081.js]
[test_1085291.js]
[test_1105208.js]
[test_adaptive.js]
# Bug 676989: test hangs consistently on Android
skip-if = os == "android"

View File

@ -8,33 +8,27 @@ Components.utils.import("resource://gre/modules/devtools/event-emitter.js");
const EXPORTED_SYMBOLS = ["Simulator"];
function getVersionNumber(fullVersion) {
return fullVersion.match(/(\d+\.\d+)/)[0];
}
const Simulator = {
_simulators: {},
register: function (label, simulator) {
register: function (name, simulator) {
// simulators register themselves as "Firefox OS X.Y"
let versionNumber = getVersionNumber(label);
this._simulators[versionNumber] = simulator;
this.emit("register", versionNumber);
this._simulators[name] = simulator;
this.emit("register", name);
},
unregister: function (label) {
let versionNumber = getVersionNumber(label);
delete this._simulators[versionNumber];
this.emit("unregister", versionNumber);
unregister: function (name) {
delete this._simulators[name];
this.emit("unregister", name);
},
availableVersions: function () {
availableNames: function () {
return Object.keys(this._simulators).sort();
},
getByVersion: function (version) {
return this._simulators[version];
}
getByName: function (name) {
return this._simulators[name];
},
};
EventEmitter.decorate(Simulator);