mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
09cbc383e8
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;"/>
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
3
browser/modules/browsermodules.manifest
Normal file
3
browser/modules/browsermodules.manifest
Normal file
@ -0,0 +1,3 @@
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
category healthreport-js-provider-default UITourMetricsProvider resource:///modules/UITour.jsm
|
||||
#endif
|
@ -55,5 +55,9 @@ EXTRA_PP_JS_MODULES += [
|
||||
'webrtcUI.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'browsermodules.manifest',
|
||||
]
|
||||
|
||||
if CONFIG['MOZILLA_OFFICIAL']:
|
||||
DEFINES['MOZILLA_OFFICIAL'] = 1
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
});
|
||||
},
|
||||
|
||||
];
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
BIN
browser/themes/shared/magnifier.png
Normal file
BIN
browser/themes/shared/magnifier.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 371 B |
BIN
browser/themes/shared/magnifier@2x.png
Normal file
BIN
browser/themes/shared/magnifier@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 689 B |
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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_ */
|
@ -98,6 +98,10 @@ DynamicImage::OnNewSourceData()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DynamicImage::OnSurfaceDiscarded()
|
||||
{ }
|
||||
|
||||
void
|
||||
DynamicImage::SetInnerWindowID(uint64_t aInnerWindowId)
|
||||
{ }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -102,6 +102,12 @@ ImageWrapper::OnNewSourceData()
|
||||
return mInnerImage->OnNewSourceData();
|
||||
}
|
||||
|
||||
void
|
||||
ImageWrapper::OnSurfaceDiscarded()
|
||||
{
|
||||
return mInnerImage->OnSurfaceDiscarded();
|
||||
}
|
||||
|
||||
void
|
||||
ImageWrapper::SetInnerWindowID(uint64_t aInnerWindowId)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -207,9 +207,6 @@ private: // data
|
||||
bool mNonPremult;
|
||||
bool mOptimizable;
|
||||
|
||||
/** Have we called DiscardTracker::InformAllocation()? */
|
||||
bool mInformedDiscardTracker;
|
||||
|
||||
friend class DrawableFrameRef;
|
||||
friend class RawAccessFrameRef;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -18,7 +18,6 @@ UNIFIED_SOURCES += [
|
||||
'ClippedImage.cpp',
|
||||
'DecodePool.cpp',
|
||||
'Decoder.cpp',
|
||||
'DiscardTracker.cpp',
|
||||
'DynamicImage.cpp',
|
||||
'FrameAnimator.cpp',
|
||||
'FrameBlender.cpp',
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
NSS_3_18_BETA3
|
||||
NSS_3_18_BETA4
|
||||
|
@ -10,3 +10,4 @@
|
||||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
@ -1062,9 +1062,3 @@ PK11_PrivDecrypt;
|
||||
;+ local:
|
||||
;+ *;
|
||||
;+};
|
||||
;+NSS_3.18 { # NSS 3.18 release
|
||||
;+ global:
|
||||
PK11_SetCertificateNickname;
|
||||
;+ local:
|
||||
;+ *;
|
||||
;+};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
24
toolkit/components/places/tests/unit/test_1105208.js
Normal file
24
toolkit/components/places/tests/unit/test_1105208.js
Normal 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();
|
||||
}
|
@ -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"
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user