mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Merge inbound to m-c.
This commit is contained in:
commit
4cc66c38b4
4
.gitignore
vendored
4
.gitignore
vendored
@ -47,6 +47,6 @@ parser/html/java/javaparser/
|
||||
.settings/
|
||||
|
||||
# Python virtualenv artifacts.
|
||||
python/psutil/.*.so
|
||||
python/psutil/.*.pyd
|
||||
python/psutil/*.so
|
||||
python/psutil/*.pyd
|
||||
python/psutil/build/
|
||||
|
@ -101,7 +101,7 @@
|
||||
|
||||
var gQueue = null;
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
|
@ -21,6 +21,8 @@
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
function doTest()
|
||||
{
|
||||
// a@href and its text node
|
||||
|
@ -179,6 +179,7 @@ var shell = {
|
||||
});
|
||||
|
||||
this.contentBrowser.src = homeURL;
|
||||
this.isHomeLoaded = false;
|
||||
|
||||
ppmm.addMessageListener("content-handler", this);
|
||||
},
|
||||
@ -304,6 +305,18 @@ var shell = {
|
||||
DOMApplicationRegistry.allAppsLaunchable = true;
|
||||
|
||||
this.sendEvent(window, 'ContentStart');
|
||||
|
||||
let content = this.contentBrowser.contentWindow;
|
||||
content.addEventListener('load', function shell_homeLoaded() {
|
||||
content.removeEventListener('load', shell_homeLoaded);
|
||||
shell.isHomeLoaded = true;
|
||||
|
||||
if ('pendingChromeEvents' in shell) {
|
||||
shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell));
|
||||
}
|
||||
delete shell.pendingChromeEvents;
|
||||
});
|
||||
|
||||
break;
|
||||
case 'MozApplicationManifest':
|
||||
try {
|
||||
@ -349,6 +362,15 @@ var shell = {
|
||||
},
|
||||
|
||||
sendChromeEvent: function shell_sendChromeEvent(details) {
|
||||
if (!this.isHomeLoaded) {
|
||||
if (!('pendingChromeEvents' in this)) {
|
||||
this.pendingChromeEvents = [];
|
||||
}
|
||||
|
||||
this.pendingChromeEvents.push(details);
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendEvent(getContentWindow(), "mozChromeEvent",
|
||||
ObjectWrapper.wrap(details, getContentWindow()));
|
||||
},
|
||||
|
@ -2076,21 +2076,28 @@ function BrowserOpenFileWindow()
|
||||
{
|
||||
// Get filepicker component.
|
||||
try {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
||||
fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
|
||||
nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
|
||||
fp.displayDirectory = gLastOpenDirectory.path;
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
try {
|
||||
if (fp.file)
|
||||
gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
|
||||
} catch(e) {
|
||||
const nsIFilePicker = Ci.nsIFilePicker;
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK) {
|
||||
try {
|
||||
if (fp.file) {
|
||||
gLastOpenDirectory.path =
|
||||
fp.file.parent.QueryInterface(Ci.nsILocalFile);
|
||||
}
|
||||
} catch (ex) {
|
||||
}
|
||||
openUILinkIn(fp.fileURL.spec, "current");
|
||||
}
|
||||
openUILinkIn(fp.fileURL.spec, "current");
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, gNavigatorBundle.getString("openFile"),
|
||||
nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
|
||||
nsIFilePicker.filterImages | nsIFilePicker.filterXML |
|
||||
nsIFilePicker.filterHTML);
|
||||
fp.displayDirectory = gLastOpenDirectory.path;
|
||||
fp.open(fpCallback);
|
||||
} catch (ex) {
|
||||
}
|
||||
}
|
||||
|
@ -112,15 +112,22 @@ const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
function onChooseFile()
|
||||
{
|
||||
try {
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
||||
fp.init(window, dialog.bundle.getString("chooseFileDialogTitle"), nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText |
|
||||
nsIFilePicker.filterAll | nsIFilePicker.filterImages | nsIFilePicker.filterXML);
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK && fp.fileURL.spec &&
|
||||
fp.fileURL.spec.length > 0) {
|
||||
dialog.input.value = fp.fileURL.spec;
|
||||
}
|
||||
doEnabling();
|
||||
};
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK && fp.fileURL.spec && fp.fileURL.spec.length > 0)
|
||||
dialog.input.value = fp.fileURL.spec;
|
||||
fp.init(window, dialog.bundle.getString("chooseFileDialogTitle"),
|
||||
nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
|
||||
nsIFilePicker.filterImages | nsIFilePicker.filterXML |
|
||||
nsIFilePicker.filterHTML);
|
||||
fp.open(fpCallback);
|
||||
} catch (ex) {
|
||||
}
|
||||
catch(ex) {
|
||||
}
|
||||
doEnabling();
|
||||
}
|
||||
|
@ -761,31 +761,33 @@ function getSelectedRow(tree)
|
||||
return (rows.length == 1) ? rows[0] : -1;
|
||||
}
|
||||
|
||||
function selectSaveFolder()
|
||||
function selectSaveFolder(aCallback)
|
||||
{
|
||||
const nsILocalFile = Components.interfaces.nsILocalFile;
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
let titleText = gBundle.getString("mediaSelectFolder");
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK) {
|
||||
aCallback(fp.file.QueryInterface(nsILocalFile));
|
||||
} else {
|
||||
aCallback(null);
|
||||
}
|
||||
};
|
||||
|
||||
var titleText = gBundle.getString("mediaSelectFolder");
|
||||
fp.init(window, titleText, nsIFilePicker.modeGetFolder);
|
||||
try {
|
||||
var prefs = Components.classes[PREFERENCES_CONTRACTID]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
var initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
|
||||
if (initialDir)
|
||||
fp.displayDirectory = initialDir;
|
||||
}
|
||||
catch (ex) { }
|
||||
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
var ret = fp.show();
|
||||
|
||||
if (ret == nsIFilePicker.returnOK)
|
||||
return fp.file.QueryInterface(nsILocalFile);
|
||||
return null;
|
||||
try {
|
||||
let prefs = Components.classes[PREFERENCES_CONTRACTID].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
let initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
|
||||
if (initialDir) {
|
||||
fp.displayDirectory = initialDir;
|
||||
}
|
||||
} catch (ex) {
|
||||
}
|
||||
fp.open(fpCallback);
|
||||
}
|
||||
|
||||
function saveMedia()
|
||||
@ -807,37 +809,39 @@ function saveMedia()
|
||||
|
||||
saveURL(url, null, titleKey, false, false, makeURI(item.baseURI));
|
||||
}
|
||||
}
|
||||
else {
|
||||
var odir = selectSaveFolder();
|
||||
|
||||
var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
|
||||
internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
|
||||
aChosenData, aBaseURI);
|
||||
}
|
||||
|
||||
for (var i = 0; i < rowArray.length; i++) {
|
||||
var v = rowArray[i];
|
||||
var dir = odir.clone();
|
||||
var item = gImageView.data[v][COL_IMAGE_NODE];
|
||||
var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
|
||||
var uri = makeURI(uriString);
|
||||
|
||||
try {
|
||||
uri.QueryInterface(Components.interfaces.nsIURL);
|
||||
dir.append(decodeURIComponent(uri.fileName));
|
||||
} else {
|
||||
selectSaveFolder(function(aDirectory) {
|
||||
if (aDirectory) {
|
||||
var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
|
||||
internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
|
||||
aChosenData, aBaseURI);
|
||||
};
|
||||
|
||||
for (var i = 0; i < rowArray.length; i++) {
|
||||
var v = rowArray[i];
|
||||
var dir = aDirectory.clone();
|
||||
var item = gImageView.data[v][COL_IMAGE_NODE];
|
||||
var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
|
||||
var uri = makeURI(uriString);
|
||||
|
||||
try {
|
||||
uri.QueryInterface(Components.interfaces.nsIURL);
|
||||
dir.append(decodeURIComponent(uri.fileName));
|
||||
} catch(ex) {
|
||||
/* data: uris */
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
|
||||
} else {
|
||||
// This delay is a hack which prevents the download manager
|
||||
// from opening many times. See bug 377339.
|
||||
setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
|
||||
makeURI(item.baseURI));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(ex) { /* data: uris */ }
|
||||
|
||||
if (i == 0)
|
||||
saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
|
||||
else {
|
||||
// This delay is a hack which prevents the download manager
|
||||
// from opening many times. See bug 377339.
|
||||
setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
|
||||
makeURI(item.baseURI));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,26 +151,28 @@ let gSyncUtils = {
|
||||
let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
|
||||
let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
|
||||
this._preparePPiframe(elid, function(iframe) {
|
||||
let filepicker = Cc["@mozilla.org/filepicker;1"]
|
||||
.createInstance(Ci.nsIFilePicker);
|
||||
filepicker.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
|
||||
filepicker.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
filepicker.defaultString = defaultSaveName;
|
||||
let rv = filepicker.show();
|
||||
if (rv == Ci.nsIFilePicker.returnOK
|
||||
|| rv == Ci.nsIFilePicker.returnReplace) {
|
||||
let stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(filepicker.file, -1, 0600, 0);
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnOK ||
|
||||
aResult == Ci.nsIFilePicker.returnReplace) {
|
||||
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(fp.file, -1, 0600, 0);
|
||||
|
||||
let serializer = new XMLSerializer();
|
||||
let output = serializer.serializeToString(iframe.contentDocument);
|
||||
output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
|
||||
'"DTD/xhtml1-strict.dtd">');
|
||||
output = Weave.Utils.encodeUTF8(output);
|
||||
stream.write(output, output.length);
|
||||
}
|
||||
let serializer = new XMLSerializer();
|
||||
let output = serializer.serializeToString(iframe.contentDocument);
|
||||
output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
|
||||
'"DTD/xhtml1-strict.dtd">');
|
||||
output = Weave.Utils.encodeUTF8(output);
|
||||
stream.write(output, output.length);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
fp.defaultString = defaultSaveName;
|
||||
fp.open(fpCallback);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
@ -836,6 +836,9 @@
|
||||
if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
|
||||
return;
|
||||
|
||||
if (!aForceUpdate)
|
||||
TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
|
||||
|
||||
var oldTab = this.mCurrentTab;
|
||||
|
||||
// Preview mode should not reset the owner
|
||||
@ -1015,6 +1018,9 @@
|
||||
fm.setFocus(newBrowser, focusFlags);
|
||||
} while (false);
|
||||
}
|
||||
|
||||
if (!aForceUpdate)
|
||||
TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -182,12 +182,16 @@
|
||||
if (domain[0] != "[") {
|
||||
try {
|
||||
baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
|
||||
if (!domain.contains(baseDomain)) {
|
||||
// getBaseDomainFromHost converts its resultant to ACE.
|
||||
let IDNService = Cc["@mozilla.org/network/idn-service;1"]
|
||||
.getService(Ci.nsIIDNService);
|
||||
baseDomain = IDNService.convertACEtoUTF8(baseDomain);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (baseDomain != domain) {
|
||||
let segments = function (s) s.replace(/[^.]*/g, "").length + 1;
|
||||
let subSegments = segments(domain) - segments(baseDomain);
|
||||
subDomain = domain.match(new RegExp("(?:[^.]*\.){" + subSegments + "}"))[0];
|
||||
subDomain = domain.slice(0, -baseDomain.length);
|
||||
}
|
||||
|
||||
let rangeLength = preDomain.length + subDomain.length;
|
||||
|
@ -708,46 +708,53 @@ FeedWriter.prototype = {
|
||||
|
||||
/**
|
||||
* Displays a prompt from which the user may choose a (client) feed reader.
|
||||
* @return - true if a feed reader was selected, false otherwise.
|
||||
* @param aCallback the callback method, passes in true if a feed reader was
|
||||
* selected, false otherwise.
|
||||
*/
|
||||
_chooseClientApp: function FW__chooseClientApp() {
|
||||
_chooseClientApp: function FW__chooseClientApp(aCallback) {
|
||||
try {
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(this._window,
|
||||
this._getString("chooseApplicationDialogTitle"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
|
||||
if (fp.show() == Ci.nsIFilePicker.returnOK) {
|
||||
this._selectedApp = fp.file;
|
||||
if (this._selectedApp) {
|
||||
// XXXben - we need to compare this with the running instance executable
|
||||
// just don't know how to do that via script...
|
||||
// XXXmano TBD: can probably add this to nsIShellService
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnOK) {
|
||||
this._selectedApp = fp.file;
|
||||
if (this._selectedApp) {
|
||||
// XXXben - we need to compare this with the running instance
|
||||
// executable just don't know how to do that via script
|
||||
// XXXmano TBD: can probably add this to nsIShellService
|
||||
#ifdef XP_WIN
|
||||
#expand if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
|
||||
#expand if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
|
||||
#else
|
||||
#ifdef XP_MACOSX
|
||||
#expand if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") {
|
||||
#expand if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") {
|
||||
#else
|
||||
#expand if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
|
||||
#expand if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
|
||||
#endif
|
||||
#endif
|
||||
this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
|
||||
this._selectedApp);
|
||||
this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
|
||||
this._selectedApp);
|
||||
|
||||
// Show and select the selected application menuitem
|
||||
var codeStr = "selectedAppMenuItem.hidden = false;" +
|
||||
"selectedAppMenuItem.doCommand();"
|
||||
Cu.evalInSandbox(codeStr, this._contentSandbox);
|
||||
return true;
|
||||
// Show and select the selected application menuitem
|
||||
let codeStr = "selectedAppMenuItem.hidden = false;" +
|
||||
"selectedAppMenuItem.doCommand();"
|
||||
Cu.evalInSandbox(codeStr, this._contentSandbox);
|
||||
if (aCallback) {
|
||||
aCallback(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(ex) { }
|
||||
if (aCallback) {
|
||||
aCallback(false);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
return false;
|
||||
fp.init(this._window, this._getString("chooseApplicationDialogTitle"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
fp.open(fpCallback);
|
||||
} catch(ex) {
|
||||
}
|
||||
},
|
||||
|
||||
_setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) {
|
||||
@ -833,10 +840,14 @@ FeedWriter.prototype = {
|
||||
*/
|
||||
var popupbox = this._handlersMenuList.firstChild.boxObject;
|
||||
popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
|
||||
if (popupbox.popupState == "hiding" && !this._chooseClientApp()) {
|
||||
// Select the (per-prefs) selected handler if no application was
|
||||
// selected
|
||||
this._setSelectedHandler(this._getFeedType());
|
||||
if (popupbox.popupState == "hiding") {
|
||||
this._chooseClientApp(function(aResult) {
|
||||
if (!aResult) {
|
||||
// Select the (per-prefs) selected handler if no application
|
||||
// was selected
|
||||
this._setSelectedHandler(this._getFeedType());
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1210,70 +1221,77 @@ FeedWriter.prototype = {
|
||||
var useAsDefault = this._getUIElement("alwaysUse").getAttribute("checked");
|
||||
|
||||
var selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList);
|
||||
let subscribeCallback = function() {
|
||||
if (selectedItem.hasAttribute("webhandlerurl")) {
|
||||
var webURI = selectedItem.getAttribute("webhandlerurl");
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "web");
|
||||
|
||||
var supportsString = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
supportsString.data = webURI;
|
||||
prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
|
||||
supportsString);
|
||||
|
||||
var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
|
||||
if (handler) {
|
||||
if (useAsDefault) {
|
||||
wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
|
||||
}
|
||||
|
||||
this._window.location.href = handler.getHandlerURI(this._window.location.href);
|
||||
}
|
||||
} else {
|
||||
switch (selectedItem.getAttribute("anonid")) {
|
||||
case "selectedAppMenuItem":
|
||||
prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
|
||||
this._selectedApp);
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "client");
|
||||
break;
|
||||
case "defaultHandlerMenuItem":
|
||||
prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
|
||||
this._defaultSystemReader);
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "client");
|
||||
break;
|
||||
case "liveBookmarksMenuItem":
|
||||
defaultHandler = "bookmarks";
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
|
||||
break;
|
||||
}
|
||||
var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
|
||||
getService(Ci.nsIFeedResultService);
|
||||
|
||||
// Pull the title and subtitle out of the document
|
||||
var feedTitle = this._document.getElementById(TITLE_ID).textContent;
|
||||
var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
|
||||
feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
|
||||
}
|
||||
|
||||
// If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
|
||||
// to either "reader" (If a web reader or if an application is selected),
|
||||
// or to "bookmarks" (if the live bookmarks option is selected).
|
||||
// Otherwise, we should set it to "ask"
|
||||
if (useAsDefault) {
|
||||
prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
|
||||
} else {
|
||||
prefs.setCharPref(getPrefActionForType(feedType), "ask");
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// Show the file picker before subscribing if the
|
||||
// choose application menuitem was chosen using the keyboard
|
||||
if (selectedItem.getAttribute("anonid") == "chooseApplicationMenuItem") {
|
||||
if (!this._chooseClientApp())
|
||||
return;
|
||||
|
||||
selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList);
|
||||
this._chooseClientApp(function(aResult) {
|
||||
if (aResult) {
|
||||
selectedItem =
|
||||
this._getSelectedItemFromMenulist(this._handlersMenuList);
|
||||
subscribeCallback();
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
subscribeCallback();
|
||||
}
|
||||
|
||||
if (selectedItem.hasAttribute("webhandlerurl")) {
|
||||
var webURI = selectedItem.getAttribute("webhandlerurl");
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "web");
|
||||
|
||||
var supportsString = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
supportsString.data = webURI;
|
||||
prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
|
||||
supportsString);
|
||||
|
||||
var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
||||
getService(Ci.nsIWebContentConverterService);
|
||||
var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
|
||||
if (handler) {
|
||||
if (useAsDefault)
|
||||
wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
|
||||
|
||||
this._window.location.href = handler.getHandlerURI(this._window.location.href);
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (selectedItem.getAttribute("anonid")) {
|
||||
case "selectedAppMenuItem":
|
||||
prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
|
||||
this._selectedApp);
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "client");
|
||||
break;
|
||||
case "defaultHandlerMenuItem":
|
||||
prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
|
||||
this._defaultSystemReader);
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "client");
|
||||
break;
|
||||
case "liveBookmarksMenuItem":
|
||||
defaultHandler = "bookmarks";
|
||||
prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
|
||||
break;
|
||||
}
|
||||
var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
|
||||
getService(Ci.nsIFeedResultService);
|
||||
|
||||
// Pull the title and subtitle out of the document
|
||||
var feedTitle = this._document.getElementById(TITLE_ID).textContent;
|
||||
var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
|
||||
feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
|
||||
}
|
||||
|
||||
// If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
|
||||
// to either "reader" (If a web reader or if an application is selected),
|
||||
// or to "bookmarks" (if the live bookmarks option is selected).
|
||||
// Otherwise, we should set it to "ask"
|
||||
if (useAsDefault)
|
||||
prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
|
||||
else
|
||||
prefs.setCharPref(getPrefActionForType(feedType), "ask");
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
|
@ -354,34 +354,39 @@ var PlacesOrganizer = {
|
||||
* Open a file-picker and import the selected file into the bookmarks store
|
||||
*/
|
||||
importFromFile: function PO_importFromFile() {
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].
|
||||
createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, PlacesUIUtils.getString("SelectImport"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
if (fp.fileURL) {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
|
||||
Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
|
||||
BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, PlacesUIUtils.getString("SelectImport"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows simple exporting of bookmarks.
|
||||
*/
|
||||
exportBookmarks: function PO_exportBookmarks() {
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].
|
||||
createInstance(Ci.nsIFilePicker);
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel) {
|
||||
let exporter =
|
||||
Cc["@mozilla.org/browser/places/import-export-service;1"].
|
||||
getService(Ci.nsIPlacesImportExportService);
|
||||
exporter.exportHTMLToFile(fp.file);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, PlacesUIUtils.getString("EnterExport"),
|
||||
Ci.nsIFilePicker.modeSave);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
fp.defaultString = "bookmarks.html";
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
var exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
|
||||
getService(Ci.nsIPlacesImportExportService);
|
||||
exporter.exportHTMLToFile(fp.file);
|
||||
}
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -441,20 +446,23 @@ var PlacesOrganizer = {
|
||||
* Prompts for a file and restores bookmarks to those in the file.
|
||||
*/
|
||||
onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() {
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel) {
|
||||
this.restoreBookmarksFromFile(fp.file);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
|
||||
PlacesUIUtils.getString("bookmarksRestoreFilterExtension"));
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterAll);
|
||||
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
||||
fp.displayDirectory = backupsDir;
|
||||
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel)
|
||||
this.restoreBookmarksFromFile(fp.file);
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -498,21 +506,23 @@ var PlacesOrganizer = {
|
||||
* of those items.
|
||||
*/
|
||||
backupBookmarks: function PO_backupBookmarks() {
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel) {
|
||||
PlacesUtils.backups.saveBookmarksToJSONFile(fp.file);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"),
|
||||
Ci.nsIFilePicker.modeSave);
|
||||
fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
|
||||
PlacesUIUtils.getString("bookmarksRestoreFilterExtension"));
|
||||
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
|
||||
fp.displayDirectory = backupsDir;
|
||||
|
||||
fp.defaultString = PlacesUtils.backups.getFilenameForDate();
|
||||
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel)
|
||||
PlacesUtils.backups.saveBookmarksToJSONFile(fp.file);
|
||||
fp.displayDirectory = backupsDir;
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
_paneDisabled: false,
|
||||
|
@ -1715,6 +1715,28 @@ var gApplicationsPane = {
|
||||
aEvent.stopPropagation();
|
||||
|
||||
var handlerApp;
|
||||
let chooseAppCallback = function(aHandlerApp) {
|
||||
// Rebuild the actions menu whether the user picked an app or canceled.
|
||||
// If they picked an app, we want to add the app to the menu and select it.
|
||||
// If they canceled, we want to go back to their previous selection.
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// If the user picked a new app from the menu, select it.
|
||||
if (aHandlerApp) {
|
||||
let typeItem = this._list.selectedItem;
|
||||
let actionsMenu =
|
||||
document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
|
||||
let menuItems = actionsMenu.menupopup.childNodes;
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i];
|
||||
if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
|
||||
actionsMenu.selectedIndex = i;
|
||||
this.onSelectAction(menuItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
#ifdef XP_WIN
|
||||
var params = {};
|
||||
@ -1743,47 +1765,33 @@ var gApplicationsPane = {
|
||||
// Add the app to the type's list of possible handlers.
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
#else
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
var winTitle = this._prefsBundle.getString("fpTitleChooseApp");
|
||||
fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
|
||||
this._isValidHandlerExecutable(fp.file)) {
|
||||
handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
|
||||
createInstance(Ci.nsILocalHandlerApp);
|
||||
handlerApp.name = getFileDisplayName(fp.file);
|
||||
handlerApp.executable = fp.file;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
let handlerInfo = this._handledTypes[this._list.selectedItem.type];
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// Prompt the user to pick an app. If they pick one, and it's a valid
|
||||
// selection, then add it to the list of possible handlers.
|
||||
if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file &&
|
||||
this._isValidHandlerExecutable(fp.file)) {
|
||||
handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
|
||||
createInstance(Ci.nsILocalHandlerApp);
|
||||
handlerApp.name = getFileDisplayName(fp.file);
|
||||
handlerApp.executable = fp.file;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
let handlerInfo = this._handledTypes[this._list.selectedItem.type];
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
fp.open(fpCallback);
|
||||
#endif
|
||||
|
||||
// Rebuild the actions menu whether the user picked an app or canceled.
|
||||
// If they picked an app, we want to add the app to the menu and select it.
|
||||
// If they canceled, we want to go back to their previous selection.
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// If the user picked a new app from the menu, select it.
|
||||
if (handlerApp) {
|
||||
let typeItem = this._list.selectedItem;
|
||||
let actionsMenu =
|
||||
document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
|
||||
let menuItems = actionsMenu.menupopup.childNodes;
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i];
|
||||
if (menuItem.handlerApp && menuItem.handlerApp.equals(handlerApp)) {
|
||||
actionsMenu.selectedIndex = i;
|
||||
this.onSelectAction(menuItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Mark which item in the list was last selected so we can reselect it
|
||||
|
@ -1702,6 +1702,28 @@ var gApplicationsPane = {
|
||||
aEvent.stopPropagation();
|
||||
|
||||
var handlerApp;
|
||||
let chooseAppCallback = function(aHandlerApp) {
|
||||
// Rebuild the actions menu whether the user picked an app or canceled.
|
||||
// If they picked an app, we want to add the app to the menu and select it.
|
||||
// If they canceled, we want to go back to their previous selection.
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// If the user picked a new app from the menu, select it.
|
||||
if (aHandlerApp) {
|
||||
let typeItem = this._list.selectedItem;
|
||||
let actionsMenu =
|
||||
document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
|
||||
let menuItems = actionsMenu.menupopup.childNodes;
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i];
|
||||
if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
|
||||
actionsMenu.selectedIndex = i;
|
||||
this.onSelectAction(menuItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
#ifdef XP_WIN
|
||||
var params = {};
|
||||
@ -1730,47 +1752,33 @@ var gApplicationsPane = {
|
||||
// Add the app to the type's list of possible handlers.
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
#else
|
||||
var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
var winTitle = this._prefsBundle.getString("fpTitleChooseApp");
|
||||
fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
|
||||
this._isValidHandlerExecutable(fp.file)) {
|
||||
handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
|
||||
createInstance(Ci.nsILocalHandlerApp);
|
||||
handlerApp.name = getFileDisplayName(fp.file);
|
||||
handlerApp.executable = fp.file;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
let handlerInfo = this._handledTypes[this._list.selectedItem.type];
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
|
||||
chooseAppCallback(handlerApp);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// Prompt the user to pick an app. If they pick one, and it's a valid
|
||||
// selection, then add it to the list of possible handlers.
|
||||
if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file &&
|
||||
this._isValidHandlerExecutable(fp.file)) {
|
||||
handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
|
||||
createInstance(Ci.nsILocalHandlerApp);
|
||||
handlerApp.name = getFileDisplayName(fp.file);
|
||||
handlerApp.executable = fp.file;
|
||||
|
||||
// Add the app to the type's list of possible handlers.
|
||||
let handlerInfo = this._handledTypes[this._list.selectedItem.type];
|
||||
handlerInfo.addPossibleApplicationHandler(handlerApp);
|
||||
}
|
||||
fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterApps);
|
||||
fp.open(fpCallback);
|
||||
#endif
|
||||
|
||||
// Rebuild the actions menu whether the user picked an app or canceled.
|
||||
// If they picked an app, we want to add the app to the menu and select it.
|
||||
// If they canceled, we want to go back to their previous selection.
|
||||
this.rebuildActionsMenu();
|
||||
|
||||
// If the user picked a new app from the menu, select it.
|
||||
if (handlerApp) {
|
||||
let typeItem = this._list.selectedItem;
|
||||
let actionsMenu =
|
||||
document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
|
||||
let menuItems = actionsMenu.menupopup.childNodes;
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i];
|
||||
if (menuItem.handlerApp && menuItem.handlerApp.equals(handlerApp)) {
|
||||
actionsMenu.selectedIndex = i;
|
||||
this.onSelectAction(menuItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Mark which item in the list was last selected so we can reselect it
|
||||
|
@ -258,17 +258,29 @@ var gMainPane = {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
const nsILocalFile = Components.interfaces.nsILocalFile;
|
||||
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
var bundlePreferences = document.getElementById("bundlePreferences");
|
||||
var title = bundlePreferences.getString("chooseDownloadFolderTitle");
|
||||
let bundlePreferences = document.getElementById("bundlePreferences");
|
||||
let title = bundlePreferences.getString("chooseDownloadFolderTitle");
|
||||
let folderListPref = document.getElementById("browser.download.folderList");
|
||||
let currentDirPref = this._indexToFolder(folderListPref.value); // file
|
||||
let defDownloads = this._indexToFolder(1); // file
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK) {
|
||||
let file = fp.file.QueryInterface(nsILocalFile);
|
||||
let downloadDirPref = document.getElementById("browser.download.dir");
|
||||
|
||||
downloadDirPref.value = file;
|
||||
folderListPref.value = this._folderToIndex(file);
|
||||
// Note, the real prefs will not be updated yet, so dnld manager's
|
||||
// userDownloadsDirectory may not return the right folder after
|
||||
// this code executes. displayDownloadDirPref will be called on
|
||||
// the assignment above to update the UI.
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
fp.init(window, title, nsIFilePicker.modeGetFolder);
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
|
||||
var folderListPref = document.getElementById("browser.download.folderList");
|
||||
var currentDirPref = this._indexToFolder(folderListPref.value); // file
|
||||
var defDownloads = this._indexToFolder(1); // file
|
||||
|
||||
// First try to open what's currently configured
|
||||
if (currentDirPref && currentDirPref.exists()) {
|
||||
fp.displayDirectory = currentDirPref;
|
||||
@ -279,18 +291,7 @@ var gMainPane = {
|
||||
else {
|
||||
fp.displayDirectory = this._indexToFolder(0);
|
||||
}
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
var file = fp.file.QueryInterface(nsILocalFile);
|
||||
var currentDirPref = document.getElementById("browser.download.dir");
|
||||
currentDirPref.value = file;
|
||||
var folderListPref = document.getElementById("browser.download.folderList");
|
||||
folderListPref.value = this._folderToIndex(file);
|
||||
// Note, the real prefs will not be updated yet, so dnld manager's
|
||||
// userDownloadsDirectory may not return the right folder after
|
||||
// this code executes. displayDownloadDirPref will be called on
|
||||
// the assignment above to update the UI.
|
||||
}
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -255,17 +255,29 @@ var gMainPane = {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
const nsILocalFile = Components.interfaces.nsILocalFile;
|
||||
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
.createInstance(nsIFilePicker);
|
||||
var bundlePreferences = document.getElementById("bundlePreferences");
|
||||
var title = bundlePreferences.getString("chooseDownloadFolderTitle");
|
||||
let bundlePreferences = document.getElementById("bundlePreferences");
|
||||
let title = bundlePreferences.getString("chooseDownloadFolderTitle");
|
||||
let folderListPref = document.getElementById("browser.download.folderList");
|
||||
let currentDirPref = this._indexToFolder(folderListPref.value); // file
|
||||
let defDownloads = this._indexToFolder(1); // file
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == nsIFilePicker.returnOK) {
|
||||
let file = fp.file.QueryInterface(nsILocalFile);
|
||||
let downloadDirPref = document.getElementById("browser.download.dir");
|
||||
|
||||
downloadDirPref.value = file;
|
||||
folderListPref.value = this._folderToIndex(file);
|
||||
// Note, the real prefs will not be updated yet, so dnld manager's
|
||||
// userDownloadsDirectory may not return the right folder after
|
||||
// this code executes. displayDownloadDirPref will be called on
|
||||
// the assignment above to update the UI.
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
fp.init(window, title, nsIFilePicker.modeGetFolder);
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
|
||||
var folderListPref = document.getElementById("browser.download.folderList");
|
||||
var currentDirPref = this._indexToFolder(folderListPref.value); // file
|
||||
var defDownloads = this._indexToFolder(1); // file
|
||||
|
||||
// First try to open what's currently configured
|
||||
if (currentDirPref && currentDirPref.exists()) {
|
||||
fp.displayDirectory = currentDirPref;
|
||||
@ -276,18 +288,7 @@ var gMainPane = {
|
||||
else {
|
||||
fp.displayDirectory = this._indexToFolder(0);
|
||||
}
|
||||
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
var file = fp.file.QueryInterface(nsILocalFile);
|
||||
var currentDirPref = document.getElementById("browser.download.dir");
|
||||
currentDirPref.value = file;
|
||||
var folderListPref = document.getElementById("browser.download.folderList");
|
||||
folderListPref.value = this._folderToIndex(file);
|
||||
// Note, the real prefs will not be updated yet, so dnld manager's
|
||||
// userDownloadsDirectory may not return the right folder after
|
||||
// this code executes. displayDownloadDirPref will be called on
|
||||
// the assignment above to update the UI.
|
||||
}
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -668,15 +668,7 @@ var Scratchpad = {
|
||||
*/
|
||||
openFile: function SP_openFile(aIndex)
|
||||
{
|
||||
let fp;
|
||||
if (!aIndex && aIndex !== 0) {
|
||||
fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, this.strings.GetStringFromName("openFile.title"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.defaultString = "";
|
||||
}
|
||||
|
||||
if (aIndex > -1 || fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
let promptCallback = function(aFile) {
|
||||
this.promptSave(function(aCloseFile, aSaved, aStatus) {
|
||||
let shouldOpen = aCloseFile;
|
||||
if (aSaved && !Components.isSuccessCode(aStatus)) {
|
||||
@ -687,8 +679,8 @@ var Scratchpad = {
|
||||
this._skipClosePrompt = true;
|
||||
|
||||
let file;
|
||||
if (fp) {
|
||||
file = fp.file;
|
||||
if (aFile) {
|
||||
file = aFile;
|
||||
} else {
|
||||
file = Components.classes["@mozilla.org/file/local;1"].
|
||||
createInstance(Components.interfaces.nsILocalFile);
|
||||
@ -701,6 +693,22 @@ var Scratchpad = {
|
||||
this.setRecentFile(file);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
if (aIndex > -1) {
|
||||
promptCallback();
|
||||
} else {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel) {
|
||||
promptCallback(fp.file);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, this.strings.GetStringFromName("openFile.title"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.defaultString = "";
|
||||
fp.open(fpCallback);
|
||||
}
|
||||
},
|
||||
|
||||
@ -894,22 +902,25 @@ var Scratchpad = {
|
||||
saveFileAs: function SP_saveFileAs(aCallback)
|
||||
{
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult != Ci.nsIFilePicker.returnCancel) {
|
||||
this.setFilename(fp.file.path);
|
||||
this.exportToFile(fp.file, true, false, function(aStatus) {
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
this.editor.dirty = false;
|
||||
this.setRecentFile(fp.file);
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback(aStatus);
|
||||
}
|
||||
});
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
fp.init(window, this.strings.GetStringFromName("saveFileAs"),
|
||||
Ci.nsIFilePicker.modeSave);
|
||||
fp.defaultString = "scratchpad.js";
|
||||
if (fp.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
this.setFilename(fp.file.path);
|
||||
|
||||
this.exportToFile(fp.file, true, false, function(aStatus) {
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
this.editor.dirty = false;
|
||||
this.setRecentFile(fp.file);
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback(aStatus);
|
||||
}
|
||||
});
|
||||
}
|
||||
fp.open(fpCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -288,22 +288,24 @@ StyleEditor.prototype = {
|
||||
*/
|
||||
importFromFile: function SE_importFromFile(aFile, aParentWindow)
|
||||
{
|
||||
aFile = this._showFilePicker(aFile, false, aParentWindow);
|
||||
if (!aFile) {
|
||||
return;
|
||||
}
|
||||
this._savedFile = aFile; // remember filename for next save if any
|
||||
let callback = function(aFile) {
|
||||
if (aFile) {
|
||||
this._savedFile = aFile; // remember filename for next save if any
|
||||
|
||||
NetUtil.asyncFetch(aFile, function onAsyncFetch(aStream, aStatus) {
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
return this._signalError(LOAD_ERROR);
|
||||
NetUtil.asyncFetch(aFile, function onAsyncFetch(aStream, aStatus) {
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
return this._signalError(LOAD_ERROR);
|
||||
}
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
aStream.close();
|
||||
|
||||
this._appendNewStyleSheet(source);
|
||||
this.clearFlag(StyleEditorFlags.ERROR);
|
||||
}.bind(this));
|
||||
}
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
aStream.close();
|
||||
}.bind(this);
|
||||
|
||||
this._appendNewStyleSheet(source);
|
||||
this.clearFlag(StyleEditorFlags.ERROR);
|
||||
}.bind(this));
|
||||
this._showFilePicker(aFile, false, aParentWindow, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -553,46 +555,48 @@ StyleEditor.prototype = {
|
||||
*/
|
||||
saveToFile: function SE_saveToFile(aFile, aCallback)
|
||||
{
|
||||
aFile = this._showFilePicker(aFile || this._styleSheetFilePath, true);
|
||||
|
||||
if (!aFile) {
|
||||
if (aCallback) {
|
||||
aCallback(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._sourceEditor) {
|
||||
this._state.text = this._sourceEditor.getText();
|
||||
}
|
||||
|
||||
let ostream = FileUtils.openSafeFileOutputStream(aFile);
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let istream = converter.convertToInputStream(this._state.text);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function SE_onStreamCopied(status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
let callback = function(aReturnFile) {
|
||||
if (!aReturnFile) {
|
||||
if (aCallback) {
|
||||
aCallback(null);
|
||||
}
|
||||
this._signalError(SAVE_ERROR);
|
||||
return;
|
||||
}
|
||||
FileUtils.closeSafeFileOutputStream(ostream);
|
||||
|
||||
// remember filename for next save if any
|
||||
this._friendlyName = null;
|
||||
this._savedFile = aFile;
|
||||
this._persistExpando();
|
||||
|
||||
if (aCallback) {
|
||||
aCallback(aFile);
|
||||
if (this._sourceEditor) {
|
||||
this._state.text = this._sourceEditor.getText();
|
||||
}
|
||||
this.clearFlag(StyleEditorFlags.UNSAVED);
|
||||
this.clearFlag(StyleEditorFlags.ERROR);
|
||||
}.bind(this));
|
||||
|
||||
let ostream = FileUtils.openSafeFileOutputStream(aReturnFile);
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let istream = converter.convertToInputStream(this._state.text);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function SE_onStreamCopied(status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
if (aCallback) {
|
||||
aCallback(null);
|
||||
}
|
||||
this._signalError(SAVE_ERROR);
|
||||
return;
|
||||
}
|
||||
FileUtils.closeSafeFileOutputStream(ostream);
|
||||
|
||||
// remember filename for next save if any
|
||||
this._friendlyName = null;
|
||||
this._savedFile = aReturnFile;
|
||||
this._persistExpando();
|
||||
|
||||
if (aCallback) {
|
||||
aCallback(aReturnFile);
|
||||
}
|
||||
this.clearFlag(StyleEditorFlags.UNSAVED);
|
||||
this.clearFlag(StyleEditorFlags.ERROR);
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
this._showFilePicker(aFile || this._styleSheetFilePath, true, null, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -690,31 +694,36 @@ StyleEditor.prototype = {
|
||||
* @param nsIWindow aParentWindow
|
||||
* Optional parent window. If null the parent window of the file picker
|
||||
* will be the window of the attached input element.
|
||||
* @return nsIFile
|
||||
* The selected file or null if the user did not pick one.
|
||||
* @param aCallback
|
||||
* The callback method, which will be called passing in the selected
|
||||
* file or null if the user did not pick one.
|
||||
*/
|
||||
_showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow)
|
||||
_showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow, aCallback)
|
||||
{
|
||||
if (typeof(aFile) == "string") {
|
||||
try {
|
||||
if (Services.io.extractScheme(aFile) == "file") {
|
||||
let uri = Services.io.newURI(aFile, null, null);
|
||||
let file = uri.QueryInterface(Ci.nsIFileURL).file;
|
||||
return file;
|
||||
aCallback(file);
|
||||
return;
|
||||
}
|
||||
} catch (ex) {
|
||||
}
|
||||
try {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(aFile);
|
||||
return file;
|
||||
aCallback(file);
|
||||
return;
|
||||
} catch (ex) {
|
||||
this._signalError(aSave ? SAVE_ERROR : LOAD_ERROR);
|
||||
return null;
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (aFile) {
|
||||
return aFile;
|
||||
aCallback(aFile);
|
||||
return;
|
||||
}
|
||||
|
||||
let window = aParentWindow
|
||||
@ -723,13 +732,19 @@ StyleEditor.prototype = {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let mode = aSave ? fp.modeSave : fp.modeOpen;
|
||||
let key = aSave ? "saveStyleSheet" : "importStyleSheet";
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnCancel) {
|
||||
aCallback(null);
|
||||
} else {
|
||||
aCallback(fp.file);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, _(key + ".title"), mode);
|
||||
fp.appendFilters(_(key + ".filter"), "*.css");
|
||||
fp.appendFilters(fp.filterAll);
|
||||
|
||||
let rv = fp.show();
|
||||
return (rv == fp.returnCancel) ? null : fp.file;
|
||||
fp.open(fpCallback);
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1537,10 +1537,22 @@ public:
|
||||
* which places the viewport information in the document header instead
|
||||
* of returning it directly.
|
||||
*
|
||||
* @param aDisplayWidth width of the on-screen display area for this
|
||||
* document, in device pixels.
|
||||
* @param aDisplayHeight height of the on-screen display area for this
|
||||
* document, in device pixels.
|
||||
*
|
||||
* NOTE: If the site is optimized for mobile (via the doctype), this
|
||||
* will return viewport information that specifies default information.
|
||||
*/
|
||||
static ViewportInfo GetViewportInfo(nsIDocument* aDocument);
|
||||
static ViewportInfo GetViewportInfo(nsIDocument* aDocument,
|
||||
uint32_t aDisplayWidth,
|
||||
uint32_t aDisplayHeight);
|
||||
|
||||
/**
|
||||
* The device-pixel-to-CSS-px ratio used to adjust meta viewport values.
|
||||
*/
|
||||
static double GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget);
|
||||
|
||||
// Call EnterMicroTask when you're entering JS execution.
|
||||
// Usually the best way to do this is to use nsAutoMicroTask.
|
||||
|
@ -388,6 +388,7 @@ interface nsIXMLHttpRequest : nsISupports
|
||||
[scriptable, uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
|
||||
interface nsIXHRSendable : nsISupports {
|
||||
void getSendInfo(out nsIInputStream body,
|
||||
out uint64_t contentLength,
|
||||
out ACString contentType,
|
||||
out ACString charset);
|
||||
};
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "Layers.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -5083,7 +5085,9 @@ static void ProcessViewportToken(nsIDocument *aDocument,
|
||||
|
||||
/* static */
|
||||
ViewportInfo
|
||||
nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
nsContentUtils::GetViewportInfo(nsIDocument *aDocument,
|
||||
uint32_t aDisplayWidth,
|
||||
uint32_t aDisplayHeight)
|
||||
{
|
||||
ViewportInfo ret;
|
||||
ret.defaultZoom = 1.0;
|
||||
@ -5172,37 +5176,44 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
autoSize = true;
|
||||
}
|
||||
|
||||
// XXXjwir3:
|
||||
// See bug 706918, comment 23 for more information on this particular section
|
||||
// of the code. We're using "screen size" in place of the size of the content
|
||||
// area, because on mobile, these are close or equal. This will work for our
|
||||
// purposes (bug 706198), but it will need to be changed in the future to be
|
||||
// more correct when we bring the rest of the viewport code into platform.
|
||||
// We actually want the size of the content area, in the event that we don't
|
||||
// have any metadata about the width and/or height. On mobile, the screen size
|
||||
// and the size of the content area are very close, or the same value.
|
||||
// In XUL fennec, the content area is the size of the <browser> widget, but
|
||||
// in native fennec, the content area is the size of the Gecko LayerView
|
||||
// object.
|
||||
// Now convert the scale into device pixels per CSS pixel.
|
||||
nsIWidget *widget = WidgetForDocument(aDocument);
|
||||
double pixelRatio = widget ? GetDevicePixelsPerMetaViewportPixel(widget) : 1.0;
|
||||
scaleFloat *= pixelRatio;
|
||||
scaleMinFloat *= pixelRatio;
|
||||
scaleMaxFloat *= pixelRatio;
|
||||
|
||||
// TODO:
|
||||
// Once bug 716575 has been resolved, this code should be changed so that it
|
||||
// does the right thing on all platforms.
|
||||
nsresult result;
|
||||
int32_t screenLeft, screenTop, screenWidth, screenHeight;
|
||||
nsCOMPtr<nsIScreenManager> screenMgr =
|
||||
do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
|
||||
uint32_t width, height;
|
||||
if (autoSize) {
|
||||
// aDisplayWidth and aDisplayHeight are in device pixels; convert them to
|
||||
// CSS pixels for the viewport size.
|
||||
width = aDisplayWidth / pixelRatio;
|
||||
height = aDisplayHeight / pixelRatio;
|
||||
} else {
|
||||
nsresult widthErrorCode, heightErrorCode;
|
||||
width = widthStr.ToInteger(&widthErrorCode);
|
||||
height = heightStr.ToInteger(&heightErrorCode);
|
||||
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
|
||||
screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
|
||||
// If width or height has not been set to a valid number by this point,
|
||||
// fall back to a default value.
|
||||
bool validWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && width > 0);
|
||||
bool validHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && height > 0);
|
||||
|
||||
uint32_t width = widthStr.ToInteger(&errorCode);
|
||||
if (NS_FAILED(errorCode)) {
|
||||
if (autoSize) {
|
||||
width = screenWidth;
|
||||
} else {
|
||||
width = Preferences::GetInt("browser.viewport.desktopWidth", 0);
|
||||
if (!validWidth) {
|
||||
if (validHeight && aDisplayWidth > 0 && aDisplayHeight > 0) {
|
||||
width = uint32_t((height * aDisplayWidth) / aDisplayHeight);
|
||||
} else {
|
||||
width = Preferences::GetInt("browser.viewport.desktopWidth",
|
||||
kViewportDefaultScreenWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (!validHeight) {
|
||||
if (aDisplayWidth > 0 && aDisplayHeight > 0) {
|
||||
height = uint32_t((width * aDisplayHeight) / aDisplayWidth);
|
||||
} else {
|
||||
height = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5212,19 +5223,7 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
// Also recalculate the default zoom, if it wasn't specified in the metadata,
|
||||
// and the width is specified.
|
||||
if (scaleStr.IsEmpty() && !widthStr.IsEmpty()) {
|
||||
scaleFloat = NS_MAX(scaleFloat, (float)(screenWidth/width));
|
||||
}
|
||||
|
||||
uint32_t height = heightStr.ToInteger(&errorCode);
|
||||
|
||||
if (NS_FAILED(errorCode)) {
|
||||
height = width * ((float)screenHeight / screenWidth);
|
||||
}
|
||||
|
||||
// If height was provided by the user, but width wasn't, then we should
|
||||
// calculate the width.
|
||||
if (widthStr.IsEmpty() && !heightStr.IsEmpty()) {
|
||||
width = (uint32_t) ((height * screenWidth) / screenHeight);
|
||||
scaleFloat = NS_MAX(scaleFloat, float(aDisplayWidth) / float(width));
|
||||
}
|
||||
|
||||
height = NS_MIN(height, kViewportMaxHeight);
|
||||
@ -5233,11 +5232,11 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
// We need to perform a conversion, but only if the initial or maximum
|
||||
// scale were set explicitly by the user.
|
||||
if (!scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode)) {
|
||||
width = NS_MAX(width, (uint32_t)(screenWidth / scaleFloat));
|
||||
height = NS_MAX(height, (uint32_t)(screenHeight / scaleFloat));
|
||||
width = NS_MAX(width, (uint32_t)(aDisplayWidth / scaleFloat));
|
||||
height = NS_MAX(height, (uint32_t)(aDisplayHeight / scaleFloat));
|
||||
} else if (!maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode)) {
|
||||
width = NS_MAX(width, (uint32_t)(screenWidth / scaleMaxFloat));
|
||||
height = NS_MAX(height, (uint32_t)(screenHeight / scaleMaxFloat));
|
||||
width = NS_MAX(width, (uint32_t)(aDisplayWidth / scaleMaxFloat));
|
||||
height = NS_MAX(height, (uint32_t)(aDisplayHeight / scaleMaxFloat));
|
||||
}
|
||||
|
||||
bool allowZoom = true;
|
||||
@ -5260,6 +5259,28 @@ nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* static */
|
||||
double
|
||||
nsContentUtils::GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget)
|
||||
{
|
||||
int32_t prefValue = Preferences::GetInt("browser.viewport.scaleRatio", 0);
|
||||
if (prefValue > 0) {
|
||||
return double(prefValue) / 100.0;
|
||||
}
|
||||
|
||||
float dpi = aWidget->GetDPI();
|
||||
if (dpi < 200.0) {
|
||||
// Includes desktop displays, LDPI and MDPI Android devices
|
||||
return 1.0;
|
||||
}
|
||||
if (dpi < 300.0) {
|
||||
// Includes Nokia N900, and HDPI Android devices
|
||||
return 1.5;
|
||||
}
|
||||
// For very high-density displays like the iPhone 4, use an integer ratio.
|
||||
return floor(dpi / 150.0);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
|
||||
|
@ -374,6 +374,7 @@ nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
|
||||
uint64_t* aContentLength,
|
||||
nsACString& aContentType,
|
||||
nsACString& aCharset)
|
||||
{
|
||||
@ -383,6 +384,9 @@ nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
|
||||
rv = this->GetInternalStream(getter_AddRefs(stream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = this->GetSize(aContentLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsString contentType;
|
||||
rv = this->GetType(contentType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -103,8 +103,8 @@ nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
|
||||
// nsIXHRSendable
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFormData::GetSendInfo(nsIInputStream** aBody, nsACString& aContentType,
|
||||
nsACString& aCharset)
|
||||
nsFormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
|
||||
|
||||
@ -119,7 +119,8 @@ nsFormData::GetSendInfo(nsIInputStream** aBody, nsACString& aContentType,
|
||||
|
||||
fs.GetContentType(aContentType);
|
||||
aCharset.Truncate();
|
||||
NS_ADDREF(*aBody = fs.GetSubmissionBody());
|
||||
*aContentLength = 0;
|
||||
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2421,7 +2421,7 @@ nsXMLHttpRequest::SendAsBinary(const nsAString &aBody,
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
|
||||
GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult, uint64* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
aContentType.AssignLiteral("application/xml");
|
||||
@ -2455,46 +2455,57 @@ GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
|
||||
|
||||
output->Close();
|
||||
|
||||
uint32_t length;
|
||||
rv = storStream->GetLength(&length);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*aContentLength = length;
|
||||
|
||||
return storStream->NewInputStream(0, aResult);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(const nsAString& aString, nsIInputStream** aResult,
|
||||
GetRequestBody(const nsAString& aString, nsIInputStream** aResult, uint64* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
aContentType.AssignLiteral("text/plain");
|
||||
aCharset.AssignLiteral("UTF-8");
|
||||
|
||||
return NS_NewCStringInputStream(aResult, NS_ConvertUTF16toUTF8(aString));
|
||||
nsCString converted = NS_ConvertUTF16toUTF8(aString);
|
||||
*aContentLength = converted.Length();
|
||||
return NS_NewCStringInputStream(aResult, converted);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult,
|
||||
GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult, uint64* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
aContentType.AssignLiteral("text/plain");
|
||||
aCharset.Truncate();
|
||||
|
||||
nsresult rv = aStream->Available(aContentLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ADDREF(*aResult = aStream);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult,
|
||||
GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
return aSendable->GetSendInfo(aResult, aContentType, aCharset);
|
||||
return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult,
|
||||
GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult, uint64_t* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
aContentType.SetIsVoid(true);
|
||||
aCharset.Truncate();
|
||||
|
||||
int32_t length = aArrayBuffer->Length();
|
||||
*aContentLength = length;
|
||||
char* data = reinterpret_cast<char*>(aArrayBuffer->Data());
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
@ -2508,7 +2519,7 @@ GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult,
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
@ -2529,7 +2540,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
// document?
|
||||
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
|
||||
if (doc) {
|
||||
return GetRequestBody(doc, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
// nsISupportsString?
|
||||
@ -2538,19 +2549,19 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
nsAutoString string;
|
||||
wstr->GetData(string);
|
||||
|
||||
return GetRequestBody(string, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
// nsIInputStream?
|
||||
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
|
||||
if (stream) {
|
||||
return GetRequestBody(stream, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
// nsIXHRSendable?
|
||||
nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
|
||||
if (sendable) {
|
||||
return GetRequestBody(sendable, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
// ArrayBuffer?
|
||||
@ -2575,7 +2586,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
ac.construct(cx, obj);
|
||||
if (JS_IsArrayBufferObject(obj, cx)) {
|
||||
ArrayBuffer buf(cx, obj);
|
||||
return GetRequestBody(&buf, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(&buf, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2584,6 +2595,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
// Makes us act as if !aBody, don't upload anything
|
||||
aContentType.AssignLiteral("text/plain");
|
||||
aCharset.AssignLiteral("UTF-8");
|
||||
*aContentLength = 0;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2596,18 +2608,18 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
|
||||
nsString string;
|
||||
string.Adopt(data, len);
|
||||
|
||||
return GetRequestBody(string, aResult, aContentType, aCharset);
|
||||
return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
|
||||
const Nullable<RequestBody>& aBody,
|
||||
nsIInputStream** aResult,
|
||||
nsIInputStream** aResult, uint64* aContentLength,
|
||||
nsACString& aContentType, nsACString& aCharset)
|
||||
{
|
||||
if (aVariant) {
|
||||
return ::GetRequestBody(aVariant, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
|
||||
const RequestBody& body = aBody.Value();
|
||||
@ -2615,7 +2627,8 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
|
||||
switch (body.GetType()) {
|
||||
case nsXMLHttpRequest::RequestBody::ArrayBuffer:
|
||||
{
|
||||
return ::GetRequestBody(value.mArrayBuffer, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(value.mArrayBuffer, aResult, aContentLength,
|
||||
aContentType, aCharset);
|
||||
}
|
||||
case nsXMLHttpRequest::RequestBody::Blob:
|
||||
{
|
||||
@ -2623,16 +2636,17 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
|
||||
nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return ::GetRequestBody(sendable, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
case nsXMLHttpRequest::RequestBody::Document:
|
||||
{
|
||||
nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(value.mDocument);
|
||||
return ::GetRequestBody(document, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(document, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
case nsXMLHttpRequest::RequestBody::DOMString:
|
||||
{
|
||||
return ::GetRequestBody(*value.mString, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(*value.mString, aResult, aContentLength,
|
||||
aContentType, aCharset);
|
||||
}
|
||||
case nsXMLHttpRequest::RequestBody::FormData:
|
||||
{
|
||||
@ -2640,11 +2654,12 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
|
||||
nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mFormData, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return ::GetRequestBody(sendable, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
|
||||
}
|
||||
case nsXMLHttpRequest::RequestBody::InputStream:
|
||||
{
|
||||
return ::GetRequestBody(value.mStream, aResult, aContentType, aCharset);
|
||||
return ::GetRequestBody(value.mStream, aResult, aContentLength,
|
||||
aContentType, aCharset);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@ -2788,7 +2803,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
nsCOMPtr<nsIInputStream> postDataStream;
|
||||
|
||||
rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
|
||||
defaultContentType, charset);
|
||||
&mUploadTotal, defaultContentType, charset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (postDataStream) {
|
||||
@ -2851,9 +2866,6 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
}
|
||||
|
||||
mUploadComplete = false;
|
||||
uint64_t uploadTotal = 0;
|
||||
postDataStream->Available(&uploadTotal);
|
||||
mUploadTotal = uploadTotal;
|
||||
|
||||
// We want to use a newer version of the upload channel that won't
|
||||
// ignore the necessary headers for an empty Content-Type.
|
||||
@ -2862,7 +2874,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
|
||||
if (uploadChannel2) {
|
||||
uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
|
||||
-1, method, false);
|
||||
mUploadTotal, method, false);
|
||||
}
|
||||
else {
|
||||
// http channel doesn't support the new nsIUploadChannel2. Emulate
|
||||
@ -2872,7 +2884,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
}
|
||||
nsCOMPtr<nsIUploadChannel> uploadChannel =
|
||||
do_QueryInterface(httpChannel);
|
||||
uploadChannel->SetUploadStream(postDataStream, contentType, -1);
|
||||
uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
|
||||
// Reset the method to its original value
|
||||
httpChannel->SetRequestMethod(method);
|
||||
}
|
||||
|
@ -345,6 +345,7 @@ private:
|
||||
static nsresult GetRequestBody(nsIVariant* aVariant,
|
||||
const Nullable<RequestBody>& aBody,
|
||||
nsIInputStream** aResult,
|
||||
uint64_t* aContentLength,
|
||||
nsACString& aContentType,
|
||||
nsACString& aCharset);
|
||||
|
||||
|
@ -250,6 +250,13 @@ MOCHITEST_FILES_A = \
|
||||
file_XHRDocURI.text \
|
||||
file_XHRDocURI.text^headers^ \
|
||||
test_DOMException.html \
|
||||
test_meta_viewport0.html \
|
||||
test_meta_viewport1.html \
|
||||
test_meta_viewport2.html \
|
||||
test_meta_viewport3.html \
|
||||
test_meta_viewport4.html \
|
||||
test_meta_viewport5.html \
|
||||
test_meta_viewport6.html \
|
||||
test_mutationobservers.html \
|
||||
mutationobserver_dialog.html \
|
||||
test_bug744830.html \
|
||||
|
76
content/base/test/test_meta_viewport0.html
Normal file
76
content/base/test/test_meta_viewport0.html
Normal file
@ -0,0 +1,76 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p>No <meta name="viewport"> tag</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 0, "initial scale is unspecified");
|
||||
is(info.minZoom, 0, "minumum scale defaults to the absolute minumum");
|
||||
is(info.maxZoom, 10, "maximum scale defaults to the absolute maximum");
|
||||
is(info.width, 980, "width is the default width");
|
||||
is(info.height, 588, "height is proportional to displayHeight");
|
||||
is(info.autoSize, false, "autoSize is disabled by default");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(490, 600);
|
||||
is(info.width, 980, "width is still the default width");
|
||||
is(info.height, 1200, "height is proportional to the new displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.width, 980, "width is still the default width");
|
||||
is(info.height, 588, "height is still proportional to displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
76
content/base/test/test_meta_viewport1.html
Normal file
76
content/base/test/test_meta_viewport1.html
Normal file
@ -0,0 +1,76 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<p>width=device-width, initial-scale=1</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 1, "initial zoom is 100%");
|
||||
is(info.width, 800, "width is the same as the displayWidth");
|
||||
is(info.height, 480, "height is the same as the displayHeight");
|
||||
is(info.autoSize, true, "width=device-width enables autoSize");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(900, 600);
|
||||
is(info.width, 900, "changing the displayWidth changes the width");
|
||||
is(info.height, 600, "changing the displayHeight changes the height");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
let info = getViewportInfo(900, 600);
|
||||
is(info.defaultZoom, 1.5, "initial zoom is 150%");
|
||||
is(info.width, 600, "width equals displayWidth/1.5");
|
||||
is(info.height, 400, "height equals displayHeight/1.5");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
76
content/base/test/test_meta_viewport2.html
Normal file
76
content/base/test/test_meta_viewport2.html
Normal file
@ -0,0 +1,76 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
<p>width=device-width</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 1, "initial zoom is 100%");
|
||||
is(info.width, 800, "width is the same as the displayWidth");
|
||||
is(info.height, 480, "height is the same as the displayHeight");
|
||||
is(info.autoSize, true, "width=device-width enables autoSize");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(900, 600);
|
||||
is(info.width, 900, "changing the displayWidth changes the width");
|
||||
is(info.height, 600, "changing the displayHeight changes the height");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
let info = getViewportInfo(900, 600);
|
||||
is(info.defaultZoom, 1.5, "initial zoom is 150%");
|
||||
is(info.width, 600, "width equals displayWidth/1.5");
|
||||
is(info.height, 400, "height equals displayHeight/1.5");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
78
content/base/test/test_meta_viewport3.html
Normal file
78
content/base/test/test_meta_viewport3.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="width=320">
|
||||
</head>
|
||||
<body>
|
||||
<p>width=320</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 2.5, "initial zoom fits the displayWidth");
|
||||
is(info.width, 320, "width is set explicitly");
|
||||
is(info.height, 223, "height is at the absolute minimum");
|
||||
is(info.autoSize, false, "width=device-width enables autoSize");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(480, 800);
|
||||
is(info.defaultZoom, 1.5, "initial zoom fits the new displayWidth");
|
||||
is(info.width, 320, "explicit width is unchanged");
|
||||
is(info.height, 533, "height changes proportional to displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
// With an explicit width in CSS px, the scaleRatio has no effect.
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 2.5, "initial zoom still fits the displayWidth");
|
||||
is(info.width, 320, "width is still set explicitly");
|
||||
is(info.height, 223, "height is still minimum height");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
77
content/base/test/test_meta_viewport4.html
Normal file
77
content/base/test/test_meta_viewport4.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
<p>initial-scale=1.0, user-scalable=no</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 1, "initial zoom is set explicitly");
|
||||
is(info.width, 800, "width fits the initial zoom level");
|
||||
is(info.height, 480, "height fits the initial zoom level");
|
||||
is(info.autoSize, true, "initial-scale=1 enables autoSize");
|
||||
is(info.allowZoom, false, "zooming is explicitly disabled");
|
||||
|
||||
info = getViewportInfo(480, 800);
|
||||
is(info.defaultZoom, 1, "initial zoom is still set explicitly");
|
||||
is(info.width, 480, "width changes to match the displayWidth");
|
||||
is(info.height, 800, "height changes to match the displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.defaultZoom, 1.5, "initial zoom is adjusted for device pixel ratio");
|
||||
is(info.width, 533, "width fits the initial zoom");
|
||||
is(info.height, 320, "height fits the initial zoom");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
53
content/base/test/test_meta_viewport5.html
Normal file
53
content/base/test/test_meta_viewport5.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="user-scalable=NO">
|
||||
</head>
|
||||
<body>
|
||||
<p>user-scalable=NO</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.allowZoom, true, "user-scalable values are case-sensitive; 'NO' is not valid");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
82
content/base/test/test_meta_viewport6.html
Normal file
82
content/base/test/test_meta_viewport6.html
Normal file
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>meta viewport test</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta name="viewport" content="width=2000, minimum-scale=0.75">
|
||||
</head>
|
||||
<body>
|
||||
<p>width=2000, minimum-scale=0.75</p>
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [];
|
||||
|
||||
tests.push(function test1() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.minZoom, 0.75, "minumum scale is set explicitly");
|
||||
is(info.defaultZoom, 0.75, "initial scale is bounded by the minimum scale");
|
||||
is(info.maxZoom, 10, "maximum scale defaults to the absolute maximum");
|
||||
is(info.width, 2000, "width is set explicitly");
|
||||
is(info.height, 1200, "height is proportional to displayHeight");
|
||||
is(info.autoSize, false, "autoSize is disabled by default");
|
||||
is(info.allowZoom, true, "zooming is enabled by default");
|
||||
|
||||
info = getViewportInfo(2000, 1000);
|
||||
is(info.minZoom, 0.75, "minumum scale is still set explicitly");
|
||||
is(info.defaultZoom, 1, "initial scale fits the width");
|
||||
is(info.width, 2000, "width is set explicitly");
|
||||
is(info.height, 1000, "height is proportional to the new displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
tests.push(function test2() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
|
||||
function() {
|
||||
let info = getViewportInfo(800, 480);
|
||||
is(info.minZoom, 1.125, "minumum scale is converted to device pixel scale");
|
||||
is(info.defaultZoom, 1.125, "initial scale is bounded by the minimum scale");
|
||||
is(info.maxZoom, 15, "maximum scale defaults to the absolute maximum");
|
||||
is(info.width, 2000, "width is still set explicitly");
|
||||
is(info.height, 1200, "height is still proportional to displayHeight");
|
||||
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
|
||||
function getViewportInfo(aDisplayWidth, aDisplayHeight) {
|
||||
let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
|
||||
width = {}, height = {}, autoSize = {};
|
||||
|
||||
let cwu = SpecialPowers.getDOMWindowUtils(window);
|
||||
cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
|
||||
minZoom, maxZoom, width, height, autoSize);
|
||||
return {
|
||||
defaultZoom: defaultZoom.value,
|
||||
minZoom: minZoom.value,
|
||||
maxZoom: maxZoom.value,
|
||||
width: width.value,
|
||||
height: height.value,
|
||||
autoSize: autoSize.value,
|
||||
allowZoom: allowZoom.value
|
||||
};
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length)
|
||||
(tests.shift())();
|
||||
else
|
||||
SimpleTest.finish();
|
||||
}
|
||||
addEventListener("load", nextTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -169,7 +169,7 @@ public:
|
||||
NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
|
||||
}
|
||||
|
||||
nsIInputStream* GetSubmissionBody();
|
||||
nsIInputStream* GetSubmissionBody(uint64_t* aContentLength);
|
||||
|
||||
protected:
|
||||
|
||||
@ -201,6 +201,11 @@ private:
|
||||
* submission.
|
||||
*/
|
||||
nsCString mBoundary;
|
||||
|
||||
/**
|
||||
* The total length in bytes of the streams that make up mPostDataStream
|
||||
*/
|
||||
uint64_t mTotalLength;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -378,6 +378,7 @@ nsFSMultipartFormData::nsFSMultipartFormData(const nsACString& aCharset,
|
||||
{
|
||||
mPostDataStream =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
mTotalLength = 0;
|
||||
|
||||
mBoundary.AssignLiteral("---------------------------");
|
||||
mBoundary.AppendInt(rand());
|
||||
@ -391,7 +392,7 @@ nsFSMultipartFormData::~nsFSMultipartFormData()
|
||||
}
|
||||
|
||||
nsIInputStream*
|
||||
nsFSMultipartFormData::GetSubmissionBody()
|
||||
nsFSMultipartFormData::GetSubmissionBody(uint64_t* aContentLength)
|
||||
{
|
||||
// Finish data
|
||||
mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
|
||||
@ -400,6 +401,7 @@ nsFSMultipartFormData::GetSubmissionBody()
|
||||
// Add final data input stream
|
||||
AddPostDataStream();
|
||||
|
||||
*aContentLength = mTotalLength;
|
||||
return mPostDataStream;
|
||||
}
|
||||
|
||||
@ -513,6 +515,11 @@ nsFSMultipartFormData::AddNameFilePair(const nsAString& aName,
|
||||
AddPostDataStream();
|
||||
|
||||
mPostDataStream->AppendStream(fileStream);
|
||||
|
||||
uint64_t size;
|
||||
nsresult rv = aBlob->GetSize(&size);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mTotalLength += size;
|
||||
}
|
||||
|
||||
// CRLF after file
|
||||
@ -536,7 +543,8 @@ nsFSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
|
||||
GetContentType(contentType);
|
||||
mimeStream->AddHeader("Content-Type", contentType.get());
|
||||
mimeStream->SetAddContentLength(true);
|
||||
mimeStream->SetData(GetSubmissionBody());
|
||||
uint64_t unused;
|
||||
mimeStream->SetData(GetSubmissionBody(&unused));
|
||||
|
||||
*aPostDataStream = mimeStream.forget().get();
|
||||
|
||||
@ -554,6 +562,7 @@ nsFSMultipartFormData::AddPostDataStream()
|
||||
NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
|
||||
if (postDataChunkStream) {
|
||||
mPostDataStream->AppendStream(postDataChunkStream);
|
||||
mTotalLength += mPostDataChunk.Length();
|
||||
}
|
||||
|
||||
mPostDataChunk.Truncate();
|
||||
|
@ -1490,7 +1490,9 @@ void nsBuiltinDecoderStateMachine::Seek(double aTime)
|
||||
mSeekTime = NS_MAX(mStartTime, mSeekTime);
|
||||
LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %f)", mDecoder.get(), aTime));
|
||||
mState = DECODER_STATE_SEEKING;
|
||||
mDecoder->RecreateDecodedStream(mSeekTime - mStartTime);
|
||||
if (mDecoder->GetDecodedStream()) {
|
||||
mDecoder->RecreateDecodedStream(mSeekTime - mStartTime);
|
||||
}
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
@ -2045,7 +2047,7 @@ nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
|
||||
if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
|
||||
elapsed < TimeDuration::FromSeconds(mBufferingWait) &&
|
||||
(mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
|
||||
: (GetUndecodedData() < mBufferingWait * USECS_PER_S / 1000)) &&
|
||||
: (GetUndecodedData() < mBufferingWait * USECS_PER_S)) &&
|
||||
!resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
|
||||
!resource->IsSuspended())
|
||||
{
|
||||
@ -2217,12 +2219,21 @@ void nsBuiltinDecoderStateMachine::AdvanceFrame()
|
||||
int64_t remainingTime = AUDIO_DURATION_USECS;
|
||||
NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
|
||||
nsAutoPtr<VideoData> currentFrame;
|
||||
#ifdef PR_LOGGING
|
||||
int32_t droppedFrames = 0;
|
||||
#endif
|
||||
if (mReader->VideoQueue().GetSize() > 0) {
|
||||
VideoData* frame = mReader->VideoQueue().PeekFront();
|
||||
while (mRealTime || clock_time >= frame->mTime) {
|
||||
mVideoFrameEndTime = frame->mEndTime;
|
||||
currentFrame = frame;
|
||||
LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld", mDecoder.get(), frame->mTime));
|
||||
#ifdef PR_LOGGING
|
||||
if (droppedFrames++) {
|
||||
LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld (%d so far)",
|
||||
mDecoder.get(), frame->mTime, droppedFrames - 1));
|
||||
}
|
||||
#endif
|
||||
mReader->VideoQueue().PopFront();
|
||||
// Notify the decode thread that the video queue's buffers may have
|
||||
// free'd up space for more frames.
|
||||
@ -2388,7 +2399,7 @@ bool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering()
|
||||
{
|
||||
return !mDecodeStartTime.IsNull() &&
|
||||
mQuickBuffering &&
|
||||
(TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromSeconds(QUICK_BUFFER_THRESHOLD_USECS);
|
||||
(TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
|
||||
}
|
||||
|
||||
void nsBuiltinDecoderStateMachine::StartBuffering()
|
||||
|
@ -152,7 +152,11 @@ let DOMApplicationRegistry = {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// if first run, merge the system apps.
|
||||
if (runUpdate) {
|
||||
let file = FileUtils.getFile("coreAppsDir", ["webapps", "webapps.json"], false);
|
||||
let file;
|
||||
try {
|
||||
file = FileUtils.getFile("coreAppsDir", ["webapps", "webapps.json"], false);
|
||||
} catch(e) { }
|
||||
|
||||
if (file && file.exists) {
|
||||
// 2.a
|
||||
this._loadJSONAsync(file, (function loadCoreRegistry(aData) {
|
||||
@ -194,6 +198,8 @@ let DOMApplicationRegistry = {
|
||||
}
|
||||
this.registerAppsHandlers();
|
||||
}).bind(this));
|
||||
} else {
|
||||
this.registerAppsHandlers();
|
||||
}
|
||||
} else {
|
||||
this.registerAppsHandlers();
|
||||
@ -381,7 +387,8 @@ let DOMApplicationRegistry = {
|
||||
if (aCallback)
|
||||
aCallback(data);
|
||||
} catch (ex) {
|
||||
Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + ex);
|
||||
Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " +
|
||||
aFile.path + " " + ex);
|
||||
if (aCallback)
|
||||
aCallback(null);
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ nsDOMWindowUtils::GetDocCharsetIsForced(bool *aIsForced)
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
*aIsForced = doc &&
|
||||
doc->GetDocumentCharacterSetSource() >= kCharsetFromParentForced;
|
||||
}
|
||||
@ -263,6 +263,31 @@ nsDOMWindowUtils::SetCSSViewport(float aWidthPx, float aHeightPx)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetViewportInfo(uint32_t aDisplayWidth,
|
||||
uint32_t aDisplayHeight,
|
||||
double *aDefaultZoom, bool *aAllowZoom,
|
||||
double *aMinZoom, double *aMaxZoom,
|
||||
uint32_t *aWidth, uint32_t *aHeight,
|
||||
bool *aAutoSize)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_STATE(window);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
|
||||
NS_ENSURE_STATE(doc);
|
||||
|
||||
ViewportInfo info = nsContentUtils::GetViewportInfo(doc, aDisplayWidth, aDisplayHeight);
|
||||
*aDefaultZoom = info.defaultZoom;
|
||||
*aAllowZoom = info.allowZoom;
|
||||
*aMinZoom = info.minZoom;
|
||||
*aMaxZoom = info.maxZoom;
|
||||
*aWidth = info.width;
|
||||
*aHeight = info.height;
|
||||
*aAutoSize = info.autoSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void DestroyNsRect(void* aObject, nsIAtom* aPropertyName,
|
||||
void* aPropertyValue, void* aData)
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ interface nsIDOMTouch;
|
||||
interface nsIDOMClientRect;
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(6cf3e8f0-fb82-11e1-a21f-0800200c9a66)]
|
||||
[scriptable, uuid(90d8e97b-2c61-4c05-9f1c-e568d22f5bdc)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -103,6 +103,16 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
void setCSSViewport(in float aWidthPx, in float aHeightPx);
|
||||
|
||||
/**
|
||||
* Information retrieved from the <meta name="viewport"> tag.
|
||||
* See nsContentUtils::GetViewportInfo for more information.
|
||||
*/
|
||||
void getViewportInfo(in uint32_t aDisplayWidth, in uint32_t aDisplayHeight,
|
||||
out double aDefaultZoom, out boolean aAllowZoom,
|
||||
out double aMinZoom, out double aMaxZoom,
|
||||
out uint32_t aWidth, out uint32_t aHeight,
|
||||
out boolean aAutoSize);
|
||||
|
||||
/**
|
||||
* For any scrollable element, this allows you to override the
|
||||
* visible region and draw more than what is visible, which is
|
||||
|
@ -31,23 +31,50 @@ using namespace mozilla::ipc;
|
||||
namespace {
|
||||
|
||||
class RemoteInputStream : public nsIInputStream,
|
||||
public nsISeekableStream
|
||||
public nsISeekableStream,
|
||||
public nsIIPCSerializableInputStream
|
||||
{
|
||||
mozilla::Monitor mMonitor;
|
||||
nsCOMPtr<nsIDOMBlob> mSourceBlob;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
nsCOMPtr<nsISeekableStream> mSeekableStream;
|
||||
ActorFlavorEnum mOrigin;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
RemoteInputStream(nsIDOMBlob* aSourceBlob)
|
||||
: mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob)
|
||||
RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorFlavorEnum aOrigin)
|
||||
: mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
|
||||
mOrigin(aOrigin)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aSourceBlob);
|
||||
}
|
||||
|
||||
void
|
||||
Serialize(InputStreamParams& aParams)
|
||||
{
|
||||
nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
|
||||
MOZ_ASSERT(remote);
|
||||
|
||||
if (mOrigin == Parent) {
|
||||
aParams = RemoteInputStreamParams(
|
||||
static_cast<PBlobParent*>(remote->GetPBlob()), nullptr);
|
||||
} else {
|
||||
aParams = RemoteInputStreamParams(
|
||||
nullptr, static_cast<PBlobChild*>(remote->GetPBlob()));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Deserialize(const InputStreamParams& aParams)
|
||||
{
|
||||
// See InputStreamUtils.cpp to see how deserialization of a
|
||||
// RemoteInputStream is special-cased.
|
||||
MOZ_NOT_REACHED("RemoteInputStream should never be deserialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SetStream(nsIInputStream* aStream)
|
||||
{
|
||||
@ -234,6 +261,7 @@ NS_IMPL_THREADSAFE_RELEASE(RemoteInputStream)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
||||
NS_INTERFACE_MAP_END
|
||||
@ -421,7 +449,8 @@ private:
|
||||
MOZ_ASSERT(!mInputStream);
|
||||
MOZ_ASSERT(!mDone);
|
||||
|
||||
nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob);
|
||||
nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob,
|
||||
ActorFlavor);
|
||||
|
||||
StreamActorType* streamActor = new StreamActorType(stream);
|
||||
if (mActor->SendPBlobStreamConstructor(streamActor)) {
|
||||
|
@ -189,6 +189,9 @@ function RadioInterfaceLayer() {
|
||||
let lock = gSettingsService.createLock();
|
||||
lock.get("ril.radio.disabled", this);
|
||||
|
||||
// Read preferred network type from the setting DB.
|
||||
lock.get("ril.radio.preferredNetworkType", this);
|
||||
|
||||
// Read the APN data form the setting DB.
|
||||
lock.get("ril.data.apn", this);
|
||||
lock.get("ril.data.user", this);
|
||||
@ -470,6 +473,9 @@ RadioInterfaceLayer.prototype = {
|
||||
case "stksessionend":
|
||||
ppmm.broadcastAsyncMessage("RIL:StkSessionEnd", null);
|
||||
break;
|
||||
case "setPreferredNetworkType":
|
||||
this.handleSetPreferredNetworkType(message);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Don't know about this message type: " +
|
||||
message.rilMessageType);
|
||||
@ -643,6 +649,39 @@ RadioInterfaceLayer.prototype = {
|
||||
this.updateRILNetworkInterface();
|
||||
},
|
||||
|
||||
_preferredNetworkType: null,
|
||||
setPreferredNetworkType: function setPreferredNetworkType(value) {
|
||||
let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(value);
|
||||
if (networkType < 0) {
|
||||
gSettingsService.createLock().set("ril.radio.preferredNetworkType",
|
||||
this._preferredNetworkType || RIL.GECKO_PREFERRED_NETWORK_TYPE_DEFAULT,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (networkType == this._preferredNetworkType) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.worker.postMessage({rilMessageType: "setPreferredNetworkType",
|
||||
networkType: networkType});
|
||||
|
||||
this._ensureRadioState();
|
||||
},
|
||||
|
||||
handleSetPreferredNetworkType: function handleSetPreferredNetworkType(message) {
|
||||
if ((this._preferredNetworkType != null) && !message.success) {
|
||||
gSettingsService.createLock().set("ril.radio.preferredNetworkType",
|
||||
this._preferredNetworkType,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
||||
this._preferredNetworkType = message.networkType;
|
||||
debug("_preferredNetworkType is now " +
|
||||
RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[this._preferredNetworkType]);
|
||||
},
|
||||
|
||||
handleSignalStrengthChange: function handleSignalStrengthChange(message) {
|
||||
let voiceInfo = this.rilContext.voice;
|
||||
// TODO CDMA, EVDO, LTE, etc. (see bug 726098)
|
||||
@ -704,6 +743,11 @@ RadioInterfaceLayer.prototype = {
|
||||
// Wait for that.
|
||||
return;
|
||||
}
|
||||
if (this._preferredNetworkType == null) {
|
||||
// We haven't read the initial value from the settings DB yet.
|
||||
// Wait for that.
|
||||
return;
|
||||
}
|
||||
if (!this._sysMsgListenerReady) {
|
||||
// The UI's system app isn't ready yet for us to receive any
|
||||
// events (e.g. incoming SMS, etc.). Wait for that.
|
||||
@ -1203,6 +1247,10 @@ RadioInterfaceLayer.prototype = {
|
||||
this._radioEnabled = !aResult;
|
||||
this._ensureRadioState();
|
||||
break;
|
||||
case "ril.radio.preferredNetworkType":
|
||||
debug("'ril.radio.preferredNetworkType' is now " + aResult);
|
||||
this.setPreferredNetworkType(aResult);
|
||||
break;
|
||||
case "ril.data.enabled":
|
||||
this._oldRilDataEnabledState = this.dataCallSettings["enabled"];
|
||||
// Fall through!
|
||||
|
@ -335,18 +335,15 @@ const NETWORK_INFO_MESSAGE_TYPES = [
|
||||
NETWORK_INFO_NETWORK_SELECTION_MODE
|
||||
];
|
||||
|
||||
const PREFERRED_NETWORK_TYPE_GSM_WCDMA = 0;
|
||||
const PREFERRED_NETWORK_TYPE_GSM_ONLY = 1;
|
||||
const PREFERRED_NETWORK_TYPE_WCDMA = 2;
|
||||
const PREFERRED_NETWORK_TYPE_GSM_WCDMA_AUTO = 3;
|
||||
const PREFERRED_NETWORK_TYPE_CDMA_EVDO_AUTO = 4;
|
||||
const PREFERRED_NETWORK_TYPE_CDMA_ONLY = 5;
|
||||
const PREFERRED_NETWORK_TYPE_EVDO_ONLY = 6;
|
||||
const PREFERRED_NETWORK_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO = 7;
|
||||
const PREFERRED_NETWORK_TYPE_LTE_CDMA_EVDO = 8;
|
||||
const PREFERRED_NETWORK_TYPE_LTE_GSM_WCDMA = 9;
|
||||
const PREFERRED_NETWORK_TYPE_LTE_CMDA_EVDO_GSM_WCDMA = 10;
|
||||
const PREFERRED_NETWORK_TYPE_LTE_ONLY = 11;
|
||||
const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM = "wcdma/gsm";
|
||||
const GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY = "gsm";
|
||||
const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY = "wcdma";
|
||||
const GECKO_PREFERRED_NETWORK_TYPE_DEFAULT = GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM;
|
||||
const RIL_PREFERRED_NETWORK_TYPE_TO_GECKO = [
|
||||
GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM,
|
||||
GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY,
|
||||
GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY
|
||||
];
|
||||
|
||||
// Network registration states. See TS 27.007 7.2
|
||||
const NETWORK_CREG_STATE_NOT_SEARCHING = 0;
|
||||
|
@ -604,6 +604,12 @@ let RIL = {
|
||||
*/
|
||||
_pendingSentSmsMap: {},
|
||||
|
||||
/**
|
||||
* Index of the RIL_PREFERRED_NETWORK_TYPE_TO_GECKO. Its value should be
|
||||
* preserved over rild reset.
|
||||
*/
|
||||
preferredNetworkType: null,
|
||||
|
||||
initRILState: function initRILState() {
|
||||
/**
|
||||
* One of the RADIO_STATE_* constants.
|
||||
@ -1681,15 +1687,32 @@ let RIL = {
|
||||
/**
|
||||
* Set the preferred network type.
|
||||
*
|
||||
* @param network_type
|
||||
* The network type. One of the PREFERRED_NETWORK_TYPE_* constants.
|
||||
* @param options An object contains a valid index of
|
||||
* RIL_PREFERRED_NETWORK_TYPE_TO_GECKO as its `networkType`
|
||||
* attribute, or undefined to set current preferred network
|
||||
* type.
|
||||
*/
|
||||
setPreferredNetworkType: function setPreferredNetworkType(network_type) {
|
||||
Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE);
|
||||
Buf.writeUint32(network_type);
|
||||
setPreferredNetworkType: function setPreferredNetworkType(options) {
|
||||
if (options) {
|
||||
this.preferredNetworkType = options.networkType;
|
||||
}
|
||||
if (this.preferredNetworkType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE, options);
|
||||
Buf.writeUint32(1);
|
||||
Buf.writeUint32(this.preferredNetworkType);
|
||||
Buf.sendParcel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the preferred network type.
|
||||
*/
|
||||
getPreferredNetworkType: function getPreferredNetworkType() {
|
||||
Buf.simpleRequest(REQUEST_GET_PREFERRED_NETWORK_TYPE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Request various states about the network.
|
||||
*/
|
||||
@ -3999,8 +4022,33 @@ RIL[REQUEST_STK_SEND_ENVELOPE_COMMAND] = null;
|
||||
RIL[REQUEST_STK_SEND_TERMINAL_RESPONSE] = null;
|
||||
RIL[REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM] = null;
|
||||
RIL[REQUEST_EXPLICIT_CALL_TRANSFER] = null;
|
||||
RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = null;
|
||||
RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = null;
|
||||
RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = function REQUEST_SET_PREFERRED_NETWORK_TYPE(length, options) {
|
||||
if (options.networkType == null) {
|
||||
// The request was made by ril_worker itself automatically. Don't report.
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendDOMMessage({
|
||||
rilMessageType: "setPreferredNetworkType",
|
||||
networkType: options.networkType,
|
||||
success: options.rilRequestError == ERROR_SUCCESS
|
||||
});
|
||||
};
|
||||
RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = function REQUEST_GET_PREFERRED_NETWORK_TYPE(length, options) {
|
||||
let networkType;
|
||||
if (!options.rilRequestError) {
|
||||
networkType = RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(GECKO_PREFERRED_NETWORK_TYPE_DEFAULT);
|
||||
if (Buf.readUint32()) {
|
||||
this.preferredNetworkType = networkType = Buf.readUint32();
|
||||
}
|
||||
}
|
||||
|
||||
this.sendDOMMessage({
|
||||
rilMessageType: "getPreferredNetworkType",
|
||||
networkType: networkType,
|
||||
success: options.rilRequestError == ERROR_SUCCESS
|
||||
});
|
||||
};
|
||||
RIL[REQUEST_GET_NEIGHBORING_CELL_IDS] = null;
|
||||
RIL[REQUEST_SET_LOCATION_UPDATES] = null;
|
||||
RIL[REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE] = null;
|
||||
@ -4233,6 +4281,8 @@ RIL[UNSOLICITED_RIL_CONNECTED] = function UNSOLICITED_RIL_CONNECTED(length) {
|
||||
}
|
||||
|
||||
this.initRILState();
|
||||
|
||||
this.setPreferredNetworkType();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -83,7 +83,7 @@ CPPSRCS += \
|
||||
$(NULL)
|
||||
else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
CPPSRCS += \
|
||||
FallbackBattery.cpp \
|
||||
CocoaBattery.cpp \
|
||||
FallbackVibration.cpp \
|
||||
FallbackPower.cpp \
|
||||
FallbackScreenConfiguration.cpp \
|
||||
|
291
hal/cocoa/CocoaBattery.cpp
Normal file
291
hal/cocoa/CocoaBattery.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
|
||||
/* 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/. */
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <IOKit/ps/IOPowerSources.h>
|
||||
#import <IOKit/ps/IOPSKeys.h>
|
||||
|
||||
#include <mozilla/Hal.h>
|
||||
#include <mozilla/dom/battery/Constants.h>
|
||||
#include <mozilla/Services.h>
|
||||
|
||||
#include <nsObserverService.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
|
||||
|
||||
#ifndef kIOPSTimeRemainingUnknown
|
||||
#define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
|
||||
#endif
|
||||
#ifndef kIOPSTimeRemainingUnlimited
|
||||
#define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
|
||||
#endif
|
||||
|
||||
using namespace mozilla::dom::battery;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
|
||||
|
||||
class MacPowerInformationService
|
||||
{
|
||||
public:
|
||||
static MacPowerInformationService* GetInstance();
|
||||
static void Shutdown();
|
||||
|
||||
void BeginListening();
|
||||
void StopListening();
|
||||
|
||||
static void HandleChange(void *aContext);
|
||||
|
||||
~MacPowerInformationService();
|
||||
|
||||
private:
|
||||
MacPowerInformationService();
|
||||
|
||||
// The reference to the runloop that is notified of power changes.
|
||||
CFRunLoopSourceRef mRunLoopSource;
|
||||
|
||||
double mLevel;
|
||||
bool mCharging;
|
||||
double mRemainingTime;
|
||||
bool mShouldNotify;
|
||||
|
||||
friend void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
|
||||
|
||||
static MacPowerInformationService* sInstance;
|
||||
|
||||
static void* sIOKitFramework;
|
||||
static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
|
||||
};
|
||||
|
||||
void* MacPowerInformationService::sIOKitFramework;
|
||||
IOPSGetTimeRemainingEstimateFunc MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
|
||||
|
||||
/*
|
||||
* Implementation of mozilla::hal_impl::EnableBatteryNotifications,
|
||||
* mozilla::hal_impl::DisableBatteryNotifications,
|
||||
* and mozilla::hal_impl::GetCurrentBatteryInformation.
|
||||
*/
|
||||
|
||||
void
|
||||
EnableBatteryNotifications()
|
||||
{
|
||||
MacPowerInformationService::GetInstance()->BeginListening();
|
||||
}
|
||||
|
||||
void
|
||||
DisableBatteryNotifications()
|
||||
{
|
||||
MacPowerInformationService::GetInstance()->StopListening();
|
||||
}
|
||||
|
||||
void
|
||||
GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
|
||||
{
|
||||
MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
|
||||
|
||||
aBatteryInfo->level() = powerService->mLevel;
|
||||
aBatteryInfo->charging() = powerService->mCharging;
|
||||
aBatteryInfo->remainingTime() = powerService->mRemainingTime;
|
||||
}
|
||||
|
||||
/*
|
||||
* Following is the implementation of MacPowerInformationService.
|
||||
*/
|
||||
|
||||
MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
|
||||
|
||||
namespace {
|
||||
struct SingletonDestroyer MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(SingletonDestroyer, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
|
||||
{
|
||||
MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
|
||||
MacPowerInformationService::Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
/* static */ MacPowerInformationService*
|
||||
MacPowerInformationService::GetInstance()
|
||||
{
|
||||
if (sInstance) {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
sInstance = new MacPowerInformationService();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::Shutdown()
|
||||
{
|
||||
delete sInstance;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
MacPowerInformationService::MacPowerInformationService()
|
||||
: mRunLoopSource(nullptr)
|
||||
, mLevel(kDefaultLevel)
|
||||
, mCharging(kDefaultCharging)
|
||||
, mRemainingTime(kDefaultRemainingTime)
|
||||
, mShouldNotify(false)
|
||||
{
|
||||
// IOPSGetTimeRemainingEstimate (and the related constants) are only available
|
||||
// on 10.7, so we test for their presence at runtime.
|
||||
sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (sIOKitFramework) {
|
||||
sIOPSGetTimeRemainingEstimate =
|
||||
(IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate");
|
||||
} else {
|
||||
sIOPSGetTimeRemainingEstimate = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MacPowerInformationService::~MacPowerInformationService()
|
||||
{
|
||||
MOZ_ASSERT(!mRunLoopSource,
|
||||
"The observers have not been correctly removed! "
|
||||
"(StopListening should have been called)");
|
||||
|
||||
if (sIOKitFramework) {
|
||||
dlclose(sIOKitFramework);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::BeginListening()
|
||||
{
|
||||
// Set ourselves up to be notified about changes.
|
||||
MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
|
||||
"(StopListening should have been called)");
|
||||
|
||||
mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
|
||||
if (mRunLoopSource) {
|
||||
::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
|
||||
// Invoke our callback now so we have data if GetCurrentBatteryInformation is
|
||||
// called before a change happens.
|
||||
HandleChange(this);
|
||||
mShouldNotify = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::StopListening()
|
||||
{
|
||||
MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
|
||||
"(StopListening without BeginListening)");
|
||||
|
||||
::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
mRunLoopSource = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MacPowerInformationService::HandleChange(void* aContext) {
|
||||
MacPowerInformationService* power =
|
||||
static_cast<MacPowerInformationService*>(aContext);
|
||||
|
||||
CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
|
||||
if (!data) {
|
||||
::CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of power sources.
|
||||
CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
|
||||
if (!list) {
|
||||
::CFRelease(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default values. These will be used if there are 0 sources or we can't find
|
||||
// better information.
|
||||
double level = kDefaultLevel;
|
||||
double charging = kDefaultCharging;
|
||||
double remainingTime = kDefaultRemainingTime;
|
||||
|
||||
// Look for the first battery power source to give us the information we need.
|
||||
// Usually there's only 1 available, depending on current power source.
|
||||
for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
|
||||
CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
|
||||
CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
|
||||
if (!currPowerSourceDesc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sIOPSGetTimeRemainingEstimate) {
|
||||
// See if we can get a time estimate.
|
||||
CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
|
||||
if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
|
||||
remainingTime = kUnknownRemainingTime;
|
||||
} else {
|
||||
remainingTime = estimate;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a battery level estimate. This key is required.
|
||||
int currentCapacity = 0;
|
||||
const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
|
||||
::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, ¤tCapacity);
|
||||
|
||||
// This key is also required.
|
||||
int maxCapacity = 0;
|
||||
cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
|
||||
::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
|
||||
|
||||
if (maxCapacity > 0) {
|
||||
level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
|
||||
}
|
||||
|
||||
// Find out if we're charging.
|
||||
// This key is optional, we fallback to kDefaultCharging if the current power
|
||||
// source doesn't have that info.
|
||||
if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
|
||||
charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
bool isNewData = level != power->mLevel || charging != power->mCharging ||
|
||||
remainingTime != power->mRemainingTime;
|
||||
|
||||
power->mRemainingTime = remainingTime;
|
||||
power->mCharging = charging;
|
||||
power->mLevel = level;
|
||||
|
||||
// Notify the observers if stuff changed.
|
||||
if (power->mShouldNotify && isNewData) {
|
||||
hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
|
||||
power->mCharging,
|
||||
power->mRemainingTime));
|
||||
}
|
||||
|
||||
::CFRelease(data);
|
||||
::CFRelease(list);
|
||||
}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
@ -78,6 +78,7 @@ nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage, imgIDecoderObserver* aObserver
|
||||
, mCurrentRow(-1)
|
||||
, mLastFlushedRow(-1)
|
||||
, mImageData(nullptr)
|
||||
, mColormap(nullptr)
|
||||
, mOldColor(0)
|
||||
, mCurrentFrame(-1)
|
||||
, mCurrentPass(0)
|
||||
@ -187,6 +188,14 @@ nsresult nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
|
||||
mGIFStruct.width, mGIFStruct.height,
|
||||
format, aDepth, &mImageData, &imageDataLength,
|
||||
&mColormap, &mColormapSize);
|
||||
|
||||
// While EnsureFrame can reuse frames, we unconditionally increment
|
||||
// mGIFStruct.images_decoded when we're done with a frame, so we both can
|
||||
// and need to zero out the colormap and image data after every call to
|
||||
// EnsureFrame.
|
||||
if (NS_SUCCEEDED(rv) && mColormap) {
|
||||
memset(mColormap, 0, mColormapSize);
|
||||
}
|
||||
} else {
|
||||
// Regardless of depth of input, image is decoded into 24bit RGB
|
||||
rv = mImage.EnsureFrame(mGIFStruct.images_decoded,
|
||||
@ -198,6 +207,8 @@ nsresult nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
memset(mImageData, 0, imageDataLength);
|
||||
|
||||
mImage.SetFrameDisposalMethod(mGIFStruct.images_decoded,
|
||||
mGIFStruct.disposal_method);
|
||||
|
||||
@ -762,21 +773,45 @@ nsGIFDecoder2::WriteInternal(const char *aBuffer, uint32_t aCount)
|
||||
break;
|
||||
|
||||
case gif_extension:
|
||||
// Comment taken directly from WebKit's GIFImageReader.cpp.
|
||||
//
|
||||
// The GIF spec mandates lengths for three of the extensions below.
|
||||
// However, it's possible for GIFs in the wild to deviate. For example,
|
||||
// some GIFs that embed ICC color profiles using gif_application_extension
|
||||
// violate the spec and treat this extension block like a sort of
|
||||
// "extension + data" block, giving a size greater than 11 and filling the
|
||||
// remaining bytes with data (then following with more data blocks as
|
||||
// needed), instead of placing a true data block just after the 11 byte
|
||||
// extension block.
|
||||
//
|
||||
// Accordingly, if the specified length is larger than the required value,
|
||||
// we use it. If it's smaller, then we enforce the spec value, because the
|
||||
// parsers for these extensions expect to have the specified number of
|
||||
// bytes available, and if we don't ensure that, they could read off the
|
||||
// end of the heap buffer. (In this case, it's likely the GIF is corrupt
|
||||
// and we'll soon fail to decode anyway.)
|
||||
mGIFStruct.bytes_to_consume = q[1];
|
||||
if (mGIFStruct.bytes_to_consume) {
|
||||
switch (*q) {
|
||||
case GIF_GRAPHIC_CONTROL_LABEL:
|
||||
mGIFStruct.state = gif_control_extension;
|
||||
mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 4u);
|
||||
break;
|
||||
|
||||
|
||||
case GIF_APPLICATION_EXTENSION_LABEL:
|
||||
mGIFStruct.state = gif_application_extension;
|
||||
mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 11u);
|
||||
break;
|
||||
|
||||
|
||||
case GIF_PLAIN_TEXT_LABEL:
|
||||
mGIFStruct.state = gif_skip_block;
|
||||
mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 12u);
|
||||
break;
|
||||
|
||||
case GIF_COMMENT_LABEL:
|
||||
mGIFStruct.state = gif_consume_comment;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
mGIFStruct.state = gif_skip_block;
|
||||
}
|
||||
|
@ -1593,7 +1593,7 @@ RasterImage::SourceDataComplete()
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::NewSourceData(const char* aMimeType)
|
||||
RasterImage::NewSourceData()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
@ -1628,8 +1628,6 @@ RasterImage::NewSourceData(const char* aMimeType)
|
||||
mDecoded = false;
|
||||
mHasSourceData = false;
|
||||
|
||||
mSourceDataMimeType.Assign(aMimeType);
|
||||
|
||||
// We're decode-on-load here. Open up a new decoder just like what happens when
|
||||
// we call Init() for decode-on-load images.
|
||||
rv = InitDecoder(/* aDoSizeDecode = */ false);
|
||||
|
@ -269,7 +269,7 @@ public:
|
||||
nsresult SourceDataComplete();
|
||||
|
||||
/* Called for multipart images when there's a new source image to add. */
|
||||
nsresult NewSourceData(const char *aMimeType);
|
||||
nsresult NewSourceData();
|
||||
|
||||
/**
|
||||
* A hint of the number of bytes of source data that the image contains. If
|
||||
|
@ -79,11 +79,18 @@ NS_IMPL_ISUPPORTS8(imgRequest,
|
||||
nsIInterfaceRequestor,
|
||||
nsIAsyncVerifyRedirectCallback)
|
||||
|
||||
imgRequest::imgRequest(imgLoader* aLoader) :
|
||||
mLoader(aLoader), mValidator(nullptr), mImageSniffers("image-sniffing-services"),
|
||||
mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE),
|
||||
mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false),
|
||||
mIsInCache(false), mBlockingOnload(false)
|
||||
imgRequest::imgRequest(imgLoader* aLoader)
|
||||
: mLoader(aLoader)
|
||||
, mValidator(nullptr)
|
||||
, mImageSniffers("image-sniffing-services")
|
||||
, mInnerWindowId(0)
|
||||
, mCORSMode(imgIRequest::CORS_NONE)
|
||||
, mDecodeRequested(false)
|
||||
, mIsMultiPartChannel(false)
|
||||
, mGotData(false)
|
||||
, mIsInCache(false)
|
||||
, mBlockingOnload(false)
|
||||
, mResniffMimeType(false)
|
||||
{
|
||||
// Register our pref observers if we haven't yet.
|
||||
if (NS_UNLIKELY(!gInitializedPrefCaches)) {
|
||||
@ -787,22 +794,18 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
|
||||
NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
|
||||
"Already have an image for non-multipart request");
|
||||
|
||||
// If we're multipart, and our image is initialized, fix things up for another round
|
||||
// If we're multipart and about to load another image, signal so we can
|
||||
// detect the mime type in OnDataAvailable.
|
||||
if (mIsMultiPartChannel && mImage) {
|
||||
// Update the content type for this new part
|
||||
nsCOMPtr<nsIChannel> partChan(do_QueryInterface(aRequest));
|
||||
partChan->GetContentType(mContentType);
|
||||
if (mContentType.EqualsLiteral(SVG_MIMETYPE) ||
|
||||
mImage->GetType() == imgIContainer::TYPE_VECTOR) {
|
||||
// mImage won't be reusable due to format change or a new SVG part
|
||||
// Reset the tracker and forget that we have data for OnDataAvailable to
|
||||
// treat its next call as a fresh image.
|
||||
mStatusTracker = new imgStatusTracker(nullptr);
|
||||
mGotData = false;
|
||||
} else if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
// Inform the RasterImage that we have new source data
|
||||
static_cast<RasterImage*>(mImage.get())->NewSourceData(mContentType.get());
|
||||
}
|
||||
mResniffMimeType = true;
|
||||
if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
// Tell the RasterImage to reinitialize itself. We have to do this in
|
||||
// OnStartRequest so that its state machine is always in a consistent
|
||||
// state.
|
||||
// Note that if our MIME type changes, mImage will be replaced with a
|
||||
// new object.
|
||||
static_cast<RasterImage*>(mImage.get())->NewSourceData();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -935,6 +938,12 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct mimetype_closure
|
||||
{
|
||||
imgRequest* request;
|
||||
nsACString* newType;
|
||||
};
|
||||
|
||||
/* prototype for these defined below */
|
||||
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
|
||||
uint32_t toOffset, uint32_t count, uint32_t *writeCount);
|
||||
@ -953,31 +962,33 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
|
||||
nsresult rv;
|
||||
|
||||
uint16_t imageType;
|
||||
if (mGotData) {
|
||||
imageType = mImage->GetType();
|
||||
} else {
|
||||
if (!mGotData || mResniffMimeType) {
|
||||
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
|
||||
|
||||
mGotData = true;
|
||||
|
||||
mimetype_closure closure;
|
||||
nsAutoCString newType;
|
||||
closure.request = this;
|
||||
closure.newType = &newType;
|
||||
|
||||
/* look at the first few bytes and see if we can tell what the data is from that
|
||||
* since servers tend to lie. :(
|
||||
*/
|
||||
uint32_t out;
|
||||
inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
|
||||
inStr->ReadSegments(sniff_mimetype_callback, &closure, count, &out);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* NS_WARNING if the content type from the channel isn't the same if the sniffing */
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
|
||||
if (mContentType.IsEmpty()) {
|
||||
if (newType.IsEmpty()) {
|
||||
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
|
||||
|
||||
rv = NS_ERROR_FAILURE;
|
||||
if (chan) {
|
||||
rv = chan->GetContentType(mContentType);
|
||||
rv = chan->GetContentType(newType);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -993,140 +1004,162 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
|
||||
}
|
||||
|
||||
/* now we have mimetype, so we can infer the image type that we want */
|
||||
if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
|
||||
mImage = new VectorImage(mStatusTracker.forget());
|
||||
} else {
|
||||
mImage = new RasterImage(mStatusTracker.forget());
|
||||
}
|
||||
mImage->SetInnerWindowID(mInnerWindowId);
|
||||
imageType = mImage->GetType();
|
||||
// If we're a regular image and this is the first call to OnDataAvailable,
|
||||
// this will always be true. If we've resniffed our MIME type (i.e. we're a
|
||||
// multipart/x-mixed-replace image), we have to be able to switch our image
|
||||
// type and decoder.
|
||||
// We always reinitialize for SVGs, because they have no way of
|
||||
// reinitializing themselves.
|
||||
if (mContentType != newType || newType.EqualsLiteral(SVG_MIMETYPE)) {
|
||||
mContentType = newType;
|
||||
|
||||
// Notify any imgRequestProxys that are observing us that we have an Image.
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->SetImage(mImage);
|
||||
}
|
||||
|
||||
/* set our mimetype as a property */
|
||||
nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
|
||||
if (contentType) {
|
||||
contentType->SetData(mContentType);
|
||||
mProperties->Set("type", contentType);
|
||||
}
|
||||
|
||||
/* set our content disposition as a property */
|
||||
nsAutoCString disposition;
|
||||
if (chan) {
|
||||
chan->GetContentDispositionHeader(disposition);
|
||||
}
|
||||
if (!disposition.IsEmpty()) {
|
||||
nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
|
||||
if (contentDisposition) {
|
||||
contentDisposition->SetData(disposition);
|
||||
mProperties->Set("content-disposition", contentDisposition);
|
||||
// If we've resniffed our MIME type and it changed, we need to create a
|
||||
// new status tracker to give to the image, because we don't have one of
|
||||
// our own any more.
|
||||
if (mResniffMimeType) {
|
||||
NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image");
|
||||
mStatusTracker = new imgStatusTracker(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
|
||||
mResniffMimeType = false;
|
||||
|
||||
//
|
||||
// Figure out our Image initialization flags
|
||||
//
|
||||
/* now we have mimetype, so we can infer the image type that we want */
|
||||
if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
|
||||
mImage = new VectorImage(mStatusTracker.forget());
|
||||
} else {
|
||||
mImage = new RasterImage(mStatusTracker.forget());
|
||||
}
|
||||
mImage->SetInnerWindowID(mInnerWindowId);
|
||||
|
||||
// We default to the static globals
|
||||
bool isDiscardable = gDiscardable;
|
||||
bool doDecodeOnDraw = gDecodeOnDraw;
|
||||
// Notify any imgRequestProxys that are observing us that we have an Image.
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->SetImage(mImage);
|
||||
}
|
||||
|
||||
// We want UI to be as snappy as possible and not to flicker. Disable discarding
|
||||
// and decode-on-draw for chrome URLS
|
||||
bool isChrome = false;
|
||||
rv = mURI->SchemeIs("chrome", &isChrome);
|
||||
if (NS_SUCCEEDED(rv) && isChrome)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
/* set our mimetype as a property */
|
||||
nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
|
||||
if (contentType) {
|
||||
contentType->SetData(mContentType);
|
||||
mProperties->Set("type", contentType);
|
||||
}
|
||||
|
||||
// We don't want resources like the "loading" icon to be discardable or
|
||||
// decode-on-draw either.
|
||||
bool isResource = false;
|
||||
rv = mURI->SchemeIs("resource", &isResource);
|
||||
if (NS_SUCCEEDED(rv) && isResource)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
/* set our content disposition as a property */
|
||||
nsAutoCString disposition;
|
||||
if (chan) {
|
||||
chan->GetContentDispositionHeader(disposition);
|
||||
}
|
||||
if (!disposition.IsEmpty()) {
|
||||
nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
|
||||
if (contentDisposition) {
|
||||
contentDisposition->SetData(disposition);
|
||||
mProperties->Set("content-disposition", contentDisposition);
|
||||
}
|
||||
}
|
||||
|
||||
// For multipart/x-mixed-replace, we basically want a direct channel to the
|
||||
// decoder. Disable both for this case as well.
|
||||
if (mIsMultiPartChannel)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
|
||||
|
||||
// We have all the information we need
|
||||
uint32_t imageFlags = Image::INIT_FLAG_NONE;
|
||||
if (isDiscardable)
|
||||
imageFlags |= Image::INIT_FLAG_DISCARDABLE;
|
||||
if (doDecodeOnDraw)
|
||||
imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
|
||||
if (mIsMultiPartChannel)
|
||||
imageFlags |= Image::INIT_FLAG_MULTIPART;
|
||||
//
|
||||
// Figure out our Image initialization flags
|
||||
//
|
||||
|
||||
// Get our URI string
|
||||
nsAutoCString uriString;
|
||||
rv = mURI->GetSpec(uriString);
|
||||
if (NS_FAILED(rv))
|
||||
uriString.Assign("<unknown image URI>");
|
||||
// We default to the static globals
|
||||
bool isDiscardable = gDiscardable;
|
||||
bool doDecodeOnDraw = gDecodeOnDraw;
|
||||
|
||||
// Initialize the image that we created above. For RasterImages, this
|
||||
// instantiates a decoder behind the scenes, so if we don't have a decoder
|
||||
// for this mimetype we'll find out about it here.
|
||||
rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
|
||||
if (NS_FAILED(rv)) { // Probably bad mimetype
|
||||
// We want UI to be as snappy as possible and not to flicker. Disable discarding
|
||||
// and decode-on-draw for chrome URLS
|
||||
bool isChrome = false;
|
||||
rv = mURI->SchemeIs("chrome", &isChrome);
|
||||
if (NS_SUCCEEDED(rv) && isChrome)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
|
||||
this->Cancel(rv);
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
// We don't want resources like the "loading" icon to be discardable or
|
||||
// decode-on-draw either.
|
||||
bool isResource = false;
|
||||
rv = mURI->SchemeIs("resource", &isResource);
|
||||
if (NS_SUCCEEDED(rv) && isResource)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
|
||||
if (imageType == imgIContainer::TYPE_RASTER) {
|
||||
/* Use content-length as a size hint for http channels. */
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
|
||||
if (httpChannel) {
|
||||
nsAutoCString contentLength;
|
||||
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
|
||||
contentLength);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
int32_t len = contentLength.ToInteger(&rv);
|
||||
// For multipart/x-mixed-replace, we basically want a direct channel to the
|
||||
// decoder. Disable both for this case as well.
|
||||
if (mIsMultiPartChannel)
|
||||
isDiscardable = doDecodeOnDraw = false;
|
||||
|
||||
// Pass anything usable on so that the RasterImage can preallocate
|
||||
// its source buffer
|
||||
if (len > 0) {
|
||||
uint32_t sizeHint = (uint32_t) len;
|
||||
sizeHint = NS_MIN<uint32_t>(sizeHint, 20000000); /* Bound by something reasonable */
|
||||
RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
|
||||
rv = rasterImage->SetSourceSizeHint(sizeHint);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Flush memory, try to get some back, and try again
|
||||
rv = nsMemory::HeapMinimize(true);
|
||||
nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint);
|
||||
// If we've still failed at this point, things are going downhill
|
||||
if (NS_FAILED(rv) || NS_FAILED(rv2)) {
|
||||
NS_WARNING("About to hit OOM in imagelib!");
|
||||
// We have all the information we need
|
||||
uint32_t imageFlags = Image::INIT_FLAG_NONE;
|
||||
if (isDiscardable)
|
||||
imageFlags |= Image::INIT_FLAG_DISCARDABLE;
|
||||
if (doDecodeOnDraw)
|
||||
imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
|
||||
if (mIsMultiPartChannel)
|
||||
imageFlags |= Image::INIT_FLAG_MULTIPART;
|
||||
|
||||
// Get our URI string
|
||||
nsAutoCString uriString;
|
||||
rv = mURI->GetSpec(uriString);
|
||||
if (NS_FAILED(rv))
|
||||
uriString.Assign("<unknown image URI>");
|
||||
|
||||
// Initialize the image that we created above. For RasterImages, this
|
||||
// instantiates a decoder behind the scenes, so if we don't have a decoder
|
||||
// for this mimetype we'll find out about it here.
|
||||
rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
|
||||
|
||||
// We allow multipart images to fail to initialize without cancelling the
|
||||
// load because subsequent images might be fine.
|
||||
if (NS_FAILED(rv) && !mIsMultiPartChannel) { // Probably bad mimetype
|
||||
|
||||
this->Cancel(rv);
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
/* Use content-length as a size hint for http channels. */
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
|
||||
if (httpChannel) {
|
||||
nsAutoCString contentLength;
|
||||
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
|
||||
contentLength);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
int32_t len = contentLength.ToInteger(&rv);
|
||||
|
||||
// Pass anything usable on so that the RasterImage can preallocate
|
||||
// its source buffer
|
||||
if (len > 0) {
|
||||
uint32_t sizeHint = (uint32_t) len;
|
||||
sizeHint = NS_MIN<uint32_t>(sizeHint, 20000000); /* Bound by something reasonable */
|
||||
RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
|
||||
rv = rasterImage->SetSourceSizeHint(sizeHint);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Flush memory, try to get some back, and try again
|
||||
rv = nsMemory::HeapMinimize(true);
|
||||
nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint);
|
||||
// If we've still failed at this point, things are going downhill
|
||||
if (NS_FAILED(rv) || NS_FAILED(rv2)) {
|
||||
NS_WARNING("About to hit OOM in imagelib!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageType == imgIContainer::TYPE_RASTER) {
|
||||
// If we were waiting on the image to do something, now's our chance.
|
||||
if (mDecodeRequested) {
|
||||
mImage->RequestDecode();
|
||||
if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
// If we were waiting on the image to do something, now's our chance.
|
||||
if (mDecodeRequested) {
|
||||
mImage->RequestDecode();
|
||||
}
|
||||
} else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
|
||||
nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
|
||||
NS_ABORT_IF_FALSE(imageAsStream,
|
||||
"SVG-typed Image failed QI to nsIStreamListener");
|
||||
imageAsStream->OnStartRequest(aRequest, nullptr);
|
||||
}
|
||||
} else { // imageType == imgIContainer::TYPE_VECTOR
|
||||
nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
|
||||
NS_ABORT_IF_FALSE(imageAsStream,
|
||||
"SVG-typed Image failed QI to nsIStreamListener");
|
||||
imageAsStream->OnStartRequest(aRequest, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (imageType == imgIContainer::TYPE_RASTER) {
|
||||
if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
// WriteToRasterImage always consumes everything it gets
|
||||
// if it doesn't run out of memory
|
||||
uint32_t bytesRead;
|
||||
@ -1135,7 +1168,7 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
count, &bytesRead);
|
||||
NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(),
|
||||
"WriteToRasterImage should consume everything or the image must be in error!");
|
||||
} else { // imageType == imgIContainer::TYPE_VECTOR
|
||||
} else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
|
||||
nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
|
||||
rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
|
||||
sourceOffset, count);
|
||||
@ -1152,31 +1185,31 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt,
|
||||
}
|
||||
|
||||
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
|
||||
void* closure,
|
||||
void* data,
|
||||
const char* fromRawSegment,
|
||||
uint32_t toOffset,
|
||||
uint32_t count,
|
||||
uint32_t *writeCount)
|
||||
{
|
||||
imgRequest *request = static_cast<imgRequest*>(closure);
|
||||
mimetype_closure* closure = static_cast<mimetype_closure*>(data);
|
||||
|
||||
NS_ASSERTION(request, "request is null!");
|
||||
NS_ASSERTION(closure, "closure is null!");
|
||||
|
||||
if (count > 0)
|
||||
request->SniffMimeType(fromRawSegment, count);
|
||||
closure->request->SniffMimeType(fromRawSegment, count, *closure->newType);
|
||||
|
||||
*writeCount = 0;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
imgRequest::SniffMimeType(const char *buf, uint32_t len)
|
||||
imgRequest::SniffMimeType(const char *buf, uint32_t len, nsACString& newType)
|
||||
{
|
||||
imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
|
||||
imgLoader::GetMimeTypeFromContent(buf, len, newType);
|
||||
|
||||
// The vast majority of the time, imgLoader will find a gif/jpeg/png image
|
||||
// and fill mContentType with the sniffed MIME type.
|
||||
if (!mContentType.IsEmpty())
|
||||
// and fill newType with the sniffed MIME type.
|
||||
if (!newType.IsEmpty())
|
||||
return;
|
||||
|
||||
// When our sniffing fails, we want to query registered image decoders
|
||||
@ -1186,8 +1219,8 @@ imgRequest::SniffMimeType(const char *buf, uint32_t len)
|
||||
uint32_t length = sniffers.Count();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
nsresult rv =
|
||||
sniffers[i]->GetMIMETypeFromContent(nullptr, (const uint8_t *) buf, len, mContentType);
|
||||
if (NS_SUCCEEDED(rv) && !mContentType.IsEmpty()) {
|
||||
sniffers[i]->GetMIMETypeFromContent(nullptr, (const uint8_t *) buf, len, newType);
|
||||
if (NS_SUCCEEDED(rv) && !newType.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public:
|
||||
// aNotify==false still sends OnStopRequest.
|
||||
nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, bool aNotify);
|
||||
|
||||
void SniffMimeType(const char *buf, uint32_t len);
|
||||
void SniffMimeType(const char *buf, uint32_t len, nsACString& newType);
|
||||
|
||||
// Cancel, but also ensure that all work done in Init() is undone. Call this
|
||||
// only when the channel has failed to open, and so calling Cancel() on it
|
||||
@ -234,6 +234,7 @@ private:
|
||||
bool mGotData : 1;
|
||||
bool mIsInCache : 1;
|
||||
bool mBlockingOnload : 1;
|
||||
bool mResniffMimeType : 1;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -55,10 +55,12 @@ MOCHITEST_FILES = imgutils.js \
|
||||
bug671906.sjs \
|
||||
test_bug671906.html \
|
||||
test_bug733553.html \
|
||||
bug733553-iframe.html \
|
||||
bug733553.sjs \
|
||||
bug733553-informant.sjs \
|
||||
animated-gif2.gif \
|
||||
invalid.jpg \
|
||||
bad.jpg \
|
||||
rillybad.jpg \
|
||||
test_bug767779.html \
|
||||
bug767779.sjs \
|
||||
animated-gif_trailing-garbage.gif \
|
||||
|
BIN
image/test/mochitest/bad.jpg
Normal file
BIN
image/test/mochitest/bad.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@ -1,7 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 733553 iframe</title>
|
||||
<body>
|
||||
<img src="bug733553.sjs" id="image1" />
|
||||
</body>
|
||||
</html>
|
@ -2,15 +2,24 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var bodyPartIndex = 0;
|
||||
var bodyPartIndex = -1;
|
||||
var bodyParts = [
|
||||
["red.png", "image/png"],
|
||||
["animated-gif2.gif", "image/gif"],
|
||||
["red.png", "image/png"],
|
||||
["lime100x100.svg", "image/svg+xml"],
|
||||
["lime100x100.svg", "image/svg+xml"],
|
||||
["animated-gif2.gif", "image/gif"],
|
||||
["red.png", "image/png"],
|
||||
["lime100x100.svg", "image/svg+xml"]
|
||||
["damon.jpg", "image/jpeg"],
|
||||
["damon.jpg", "application/octet-stream"],
|
||||
["damon.jpg", "image/jpeg"],
|
||||
["rillybad.jpg", "application/x-unknown-content-type"],
|
||||
["damon.jpg", "image/jpeg"],
|
||||
["bad.jpg", "image/jpeg"],
|
||||
["red.png", "image/png"],
|
||||
["invalid.jpg", "image/jpeg"],
|
||||
["animated-gif2.gif", "image/gif"]
|
||||
];
|
||||
var timer = Components.classes["@mozilla.org/timer;1"];
|
||||
var partTimer = timer.createInstance(Components.interfaces.nsITimer);
|
||||
@ -34,6 +43,7 @@ function getFileAsInputStream(aFilename) {
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
setSharedState("next-part", "-1");
|
||||
response.setHeader("Content-Type",
|
||||
"multipart/x-mixed-replace;boundary=BOUNDARYOMG", false);
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
@ -47,13 +57,11 @@ function handleRequest(request, response)
|
||||
function sendParts(response) {
|
||||
let wait = false;
|
||||
let nextPart = parseInt(getSharedState("next-part"), 10);
|
||||
if (nextPart) {
|
||||
if (nextPart == bodyPartIndex) {
|
||||
// Haven't been signaled yet, remain in holding pattern
|
||||
wait = true;
|
||||
} else {
|
||||
bodyPartIndex = nextPart;
|
||||
}
|
||||
if (nextPart == bodyPartIndex) {
|
||||
// Haven't been signaled yet, remain in holding pattern
|
||||
wait = true;
|
||||
} else {
|
||||
bodyPartIndex = nextPart;
|
||||
}
|
||||
if (bodyParts.length > bodyPartIndex) {
|
||||
let callback;
|
||||
|
BIN
image/test/mochitest/rillybad.jpg
Normal file
BIN
image/test/mochitest/rillybad.jpg
Normal file
Binary file not shown.
@ -9,7 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=733553
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="initializeOnload()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=733553">Mozilla Bug 733553</a>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
@ -17,53 +17,66 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=733553
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var testIndex = 0;
|
||||
var testIndex = -1;
|
||||
var testParts = [
|
||||
[1, "red.png"],
|
||||
[40, "animated-gif2.gif"],
|
||||
[1, "red.png"],
|
||||
[100, "lime100x100.svg"],
|
||||
[100, "lime100x100.svg"],
|
||||
[40, "animated-gif2.gif"],
|
||||
[1, "red.png"]
|
||||
[1, "red.png"],
|
||||
[80, "damon.jpg"],
|
||||
[80, "damon.jpg"],
|
||||
[80, "damon.jpg"],
|
||||
// An invalid image (from bug 787899) that is further delivered with a
|
||||
// "special" bad MIME type that indicates that the necko
|
||||
// multipart/x-mixed-replace parser wasn't able to parse it.
|
||||
[0, "rillybad.jpg"],
|
||||
// Bad.jpg is bad such that it sniffs as a JPEG, and therefore doesn't change
|
||||
// what's been decoded (since the RasterImage isn't deleted), so its width
|
||||
// needs to be the same as the previous image.
|
||||
[80, "damon.jpg"],
|
||||
[80, "bad.jpg"],
|
||||
[1, "red.png"],
|
||||
[0, "invalid.jpg"],
|
||||
[40, "animated-gif2.gif"]
|
||||
];
|
||||
|
||||
// We'll append the part number to this, and tell the informant
|
||||
const BASE_URL = "bug733553-informant.sjs?";
|
||||
|
||||
function initializeOnload() {
|
||||
var iframeelem = document.getElementById('test-iframe');
|
||||
var firstimg = iframeelem.contentDocument.getElementById('image1');
|
||||
var firstimg = document.createElement('img');
|
||||
firstimg.addEventListener("load", imageLoad, false);
|
||||
firstimg.addEventListener("error", imageLoad, false);
|
||||
firstimg.src = "bug733553.sjs";
|
||||
document.getElementById('content').appendChild(firstimg);
|
||||
|
||||
// Really ready for first, but who's counting
|
||||
readyForNext();
|
||||
}
|
||||
|
||||
function readyForNext() {
|
||||
var loader = document.getElementById("loader");
|
||||
if (loader) {
|
||||
testIndex++;
|
||||
loader.src = BASE_URL + testIndex;
|
||||
}
|
||||
loader.src = BASE_URL + ++testIndex;
|
||||
}
|
||||
|
||||
function imageLoad(aEvent) {
|
||||
if (testParts.length > testIndex) {
|
||||
var [width, fileName] = testParts[testIndex];
|
||||
if (aEvent.target.width == width) {
|
||||
is(aEvent.target.width, width,
|
||||
"Test " + testIndex + " " + fileName + " width correct");
|
||||
readyForNext();
|
||||
}
|
||||
} else {
|
||||
aEvent.target.removeEventListener("load", imageLoad, false);
|
||||
var loader = document.getElementById("loader");
|
||||
readyForNext();
|
||||
var [width, fileName] = testParts[testIndex];
|
||||
is(aEvent.target.width, width,
|
||||
"Test " + testIndex + " " + fileName + " width correct");
|
||||
|
||||
if ((testParts.length - 1) == testIndex) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
readyForNext();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="content"> <!-- style="display: none" -->
|
||||
<iframe id="test-iframe" src="http://mochi.test:8888/tests/image/test/mochitest/bug733553-iframe.html" onload="initializeOnload()"></iframe>
|
||||
<iframe id="loader"></iframe>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -5,6 +5,7 @@
|
||||
include "ipc/IPCMessageUtils.h";
|
||||
|
||||
using mozilla::void_t;
|
||||
include protocol PBlob;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
@ -36,6 +37,11 @@ struct MultiplexInputStreamParams
|
||||
bool startedReadingCurrent;
|
||||
};
|
||||
|
||||
struct RemoteInputStreamParams
|
||||
{
|
||||
PBlob remoteBlob;
|
||||
};
|
||||
|
||||
union InputStreamParams
|
||||
{
|
||||
StringInputStreamParams;
|
||||
@ -44,6 +50,7 @@ union InputStreamParams
|
||||
BufferedInputStreamParams;
|
||||
MIMEInputStreamParams;
|
||||
MultiplexInputStreamParams;
|
||||
RemoteInputStreamParams;
|
||||
};
|
||||
|
||||
union OptionalInputStreamParams
|
||||
|
@ -7,16 +7,22 @@
|
||||
#include "nsIIPCSerializableInputStream.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsID.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsMIMEInputStream.h"
|
||||
#include "nsMultiplexInputStream.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
using mozilla::dom::BlobChild;
|
||||
using mozilla::dom::BlobParent;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -100,6 +106,29 @@ DeserializeInputStream(const InputStreamParams& aParams)
|
||||
serializable = do_CreateInstance(kMultiplexInputStreamCID);
|
||||
break;
|
||||
|
||||
// When the input stream already exists in this process, all we need to do
|
||||
// is retrieve the original instead of sending any data over the wire.
|
||||
case InputStreamParams::TRemoteInputStreamParams: {
|
||||
nsCOMPtr<nsIDOMBlob> domBlob;
|
||||
const RemoteInputStreamParams& params =
|
||||
aParams.get_RemoteInputStreamParams();
|
||||
|
||||
domBlob = params.remoteBlobParent() ?
|
||||
static_cast<BlobParent*>(params.remoteBlobParent())->GetBlob() :
|
||||
static_cast<BlobChild*>(params.remoteBlobChild())->GetBlob();
|
||||
|
||||
MOZ_ASSERT(domBlob, "Invalid blob contents");
|
||||
|
||||
// If fetching the internal stream fails, we ignore it and return a
|
||||
// null stream.
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsresult rv = domBlob->GetInternalStream(getter_AddRefs(stream));
|
||||
if (NS_FAILED(rv) || !stream) {
|
||||
NS_WARNING("Couldn't obtain a valid stream from the blob");
|
||||
}
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false, "Unknown params!");
|
||||
return nullptr;
|
||||
|
@ -131,6 +131,7 @@ CPPSRCS = \
|
||||
SPSProfiler.cpp \
|
||||
TokenStream.cpp \
|
||||
TestingFunctions.cpp \
|
||||
Profilers.cpp \
|
||||
LifoAlloc.cpp \
|
||||
Eval.cpp \
|
||||
MapObject.cpp \
|
||||
|
@ -226,6 +226,13 @@ ArrayLikeToIndexVector(JSContext *cx, HandleObject obj, IndexVector &indices)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IdIsInBoundsIndex(JSContext *cx, HandleObject obj, HandleId id)
|
||||
{
|
||||
uint32_t i;
|
||||
return js_IdIsIndex(id, &i) && i < ParallelArrayObject::as(obj)->outermostDimension();
|
||||
}
|
||||
|
||||
template <bool impl(JSContext *, CallArgs)>
|
||||
static inline
|
||||
JSBool NonGenericMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
@ -820,7 +827,7 @@ Class ParallelArrayObject::class_ = {
|
||||
getGeneric,
|
||||
getProperty,
|
||||
getElement,
|
||||
NULL, // getElementIfPresent
|
||||
getElementIfPresent,
|
||||
getSpecial,
|
||||
setGeneric,
|
||||
setProperty,
|
||||
@ -1034,6 +1041,15 @@ ParallelArrayObject::create(JSContext *cx, HandleObject buffer, uint32_t offset,
|
||||
result->setSlot(SLOT_BUFFER, ObjectValue(*buffer));
|
||||
result->setSlot(SLOT_BUFFER_OFFSET, Int32Value(static_cast<int32_t>(offset)));
|
||||
|
||||
// ParallelArray objects are frozen, so mark it as non-extensible here.
|
||||
Shape *empty = EmptyShape::getInitialShape(cx, &class_,
|
||||
result->getProto(), result->getParent(),
|
||||
result->getAllocKind(),
|
||||
BaseShape::NOT_EXTENSIBLE);
|
||||
if (!empty)
|
||||
return false;
|
||||
result->setLastPropertyInfallible(empty);
|
||||
|
||||
// This is usually args.rval() from build or construct.
|
||||
vp.setObject(*result);
|
||||
|
||||
@ -1430,7 +1446,14 @@ ParallelArrayObject::get(JSContext *cx, CallArgs args)
|
||||
bool
|
||||
ParallelArrayObject::dimensionsGetter(JSContext *cx, CallArgs args)
|
||||
{
|
||||
args.rval().setObject(*(as(&args.thisv().toObject())->dimensionArray()));
|
||||
RootedObject dimArray(cx, as(&args.thisv().toObject())->dimensionArray());
|
||||
RootedObject copy(cx, NewDenseCopiedArray(cx, dimArray->getDenseArrayInitializedLength(),
|
||||
dimArray->getDenseArrayElements()));
|
||||
if (!copy)
|
||||
return false;
|
||||
// Reuse the existing dimension array's type.
|
||||
copy->setType(dimArray->type());
|
||||
args.rval().setObject(*copy);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1507,7 +1530,7 @@ ParallelArrayObject::toStringBufferImpl(JSContext *cx, IndexInfo &iv, bool useLo
|
||||
if (!elem->isNullOrUndefined()) {
|
||||
if (useLocale) {
|
||||
tmp = *elem;
|
||||
JSObject *robj = ToObject(cx, tmp);
|
||||
RootedObject robj(cx, ToObject(cx, tmp));
|
||||
if (!robj)
|
||||
return false;
|
||||
|
||||
@ -1589,12 +1612,6 @@ ParallelArrayObject::lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
if (js_IdIsIndex(id, &i))
|
||||
return lookupElement(cx, obj, i, objp, propp);
|
||||
|
||||
if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||
MarkNonNativePropertyFound(obj, propp);
|
||||
objp.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->getProto());
|
||||
if (proto)
|
||||
return JSObject::lookupGeneric(cx, proto, id, objp, propp);
|
||||
@ -1637,11 +1654,27 @@ ParallelArrayObject::lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecia
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue Value,
|
||||
ParallelArrayObject::defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
|
||||
JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
uint32_t i;
|
||||
if (js_IdIsIndex(id, &i) && i < as(obj)->outermostDimension()) {
|
||||
RootedValue existingValue(cx);
|
||||
if (!as(obj)->getParallelArrayElement(cx, i, &existingValue))
|
||||
return false;
|
||||
|
||||
bool same;
|
||||
if (!SameValue(cx, value, existingValue, &same))
|
||||
return false;
|
||||
if (!same)
|
||||
return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
|
||||
} else {
|
||||
RootedValue tmp(cx, value);
|
||||
if (!setGeneric(cx, obj, id, &tmp, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
return setGenericAttributes(cx, obj, id, &attrs);
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1649,8 +1682,8 @@ ParallelArrayObject::defineProperty(JSContext *cx, HandleObject obj,
|
||||
HandlePropertyName name, HandleValue value,
|
||||
JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, NameToId(name));
|
||||
return defineGeneric(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1658,8 +1691,10 @@ ParallelArrayObject::defineElement(JSContext *cx, HandleObject obj,
|
||||
uint32_t index, HandleValue value,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, id.address()))
|
||||
return false;
|
||||
return defineGeneric(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1667,20 +1702,24 @@ ParallelArrayObject::defineSpecial(JSContext *cx, HandleObject obj,
|
||||
HandleSpecialId sid, HandleValue value,
|
||||
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, SPECIALID_TO_JSID(sid));
|
||||
return defineGeneric(cx, obj, id, value, getter, setter, attrs);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
Value idval = IdToValue(id);
|
||||
RootedValue idval(cx, IdToValue(id));
|
||||
|
||||
uint32_t index;
|
||||
if (IsDefinitelyIndex(idval, &index))
|
||||
return getElement(cx, obj, receiver, index, vp);
|
||||
|
||||
Rooted<SpecialId> sid(cx);
|
||||
if (ValueIsSpecial(obj, &idval, sid.address(), cx))
|
||||
return getSpecial(cx, obj, receiver, sid, vp);
|
||||
|
||||
JSAtom *atom = ToAtom(cx, idval);
|
||||
if (!atom)
|
||||
return false;
|
||||
@ -1696,11 +1735,6 @@ JSBool
|
||||
ParallelArrayObject::getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandlePropertyName name, MutableHandleValue vp)
|
||||
{
|
||||
if (name == cx->names().length) {
|
||||
vp.setNumber(as(obj)->outermostDimension());
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject proto(cx, obj->getProto());
|
||||
if (proto)
|
||||
return JSObject::getProperty(cx, proto, receiver, name, vp);
|
||||
@ -1718,6 +1752,23 @@ ParallelArrayObject::getElement(JSContext *cx, HandleObject obj, HandleObject re
|
||||
return as(obj)->getParallelArrayElement(cx, index, vp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, MutableHandleValue vp, bool *present)
|
||||
{
|
||||
RootedParallelArrayObject source(cx, as(obj));
|
||||
if (index < source->outermostDimension()) {
|
||||
if (!source->getParallelArrayElement(cx, index, vp))
|
||||
return false;
|
||||
*present = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
*present = false;
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
HandleSpecialId sid, MutableHandleValue vp)
|
||||
@ -1735,43 +1786,57 @@ JSBool
|
||||
ParallelArrayObject::setGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue vp, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
JS_ASSERT(!obj->isExtensible());
|
||||
|
||||
if (IdIsInBoundsIndex(cx, obj, id)) {
|
||||
if (strict)
|
||||
return JSObject::reportReadOnly(cx, id);
|
||||
if (cx->hasStrictOption())
|
||||
return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
} else {
|
||||
if (strict)
|
||||
return obj->reportNotExtensible(cx);
|
||||
if (cx->hasStrictOption())
|
||||
return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleValue vp, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, NameToId(name));
|
||||
return setGeneric(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue vp, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, id.address()))
|
||||
return false;
|
||||
return setGeneric(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
|
||||
MutableHandleValue vp, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, SPECIALID_TO_JSID(sid));
|
||||
return setGeneric(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
if (JSID_IS_ATOM(id, cx->names().length))
|
||||
*attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
else
|
||||
*attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE;
|
||||
|
||||
*attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
uint32_t i;
|
||||
if (js_IdIsIndex(id, &i))
|
||||
*attrsp |= JSPROP_ENUMERATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1779,8 +1844,7 @@ JSBool
|
||||
ParallelArrayObject::getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
if (name == cx->names().length)
|
||||
*attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
*attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1804,64 +1868,87 @@ JSBool
|
||||
ParallelArrayObject::setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
if (IdIsInBoundsIndex(cx, obj, id)) {
|
||||
unsigned attrs;
|
||||
if (!getGenericAttributes(cx, obj, id, &attrs))
|
||||
return false;
|
||||
if (*attrsp != attrs)
|
||||
return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
|
||||
}
|
||||
|
||||
return obj->reportNotExtensible(cx);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, NameToId(name));
|
||||
return setGenericAttributes(cx, obj, id, attrsp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, id.address()))
|
||||
return false;
|
||||
return setGenericAttributes(cx, obj, id, attrsp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::setSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid,
|
||||
unsigned *attrsp)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, SPECIALID_TO_JSID(sid));
|
||||
return setGenericAttributes(cx, obj, id, attrsp);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::deleteGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
MutableHandleValue rval, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
if (IdIsInBoundsIndex(cx, obj, id)) {
|
||||
if (strict)
|
||||
return obj->reportNotConfigurable(cx, id);
|
||||
if (cx->hasStrictOption()) {
|
||||
if (!obj->reportNotConfigurable(cx, id, JSREPORT_STRICT | JSREPORT_WARNING))
|
||||
return false;
|
||||
}
|
||||
|
||||
rval.setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
rval.setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleValue rval, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, NameToId(name));
|
||||
return deleteGeneric(cx, obj, id, rval, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue rval, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx);
|
||||
if (!IndexToId(cx, index, id.address()))
|
||||
return false;
|
||||
return deleteGeneric(cx, obj, id, rval, strict);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ParallelArrayObject::deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
|
||||
MutableHandleValue rval, JSBool strict)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
|
||||
return false;
|
||||
RootedId id(cx, SPECIALID_TO_JSID(sid));
|
||||
return deleteGeneric(cx, obj, id, rval, strict);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1870,13 +1957,12 @@ ParallelArrayObject::enumerate(JSContext *cx, HandleObject obj, unsigned flags,
|
||||
{
|
||||
RootedParallelArrayObject source(cx, as(obj));
|
||||
|
||||
if (flags & JSITER_HIDDEN && !props->append(NameToId(cx->names().length)))
|
||||
return false;
|
||||
|
||||
// ParallelArray objects have no holes.
|
||||
if (source->outermostDimension() > 0) {
|
||||
for (uint32_t i = 0; i < source->outermostDimension(); i++)
|
||||
props->append(INT_TO_JSID(i));
|
||||
for (uint32_t i = 0; i < source->outermostDimension(); i++) {
|
||||
if (!props->append(INT_TO_JSID(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & JSITER_OWNONLY)
|
||||
|
@ -379,6 +379,8 @@ class ParallelArrayObject : public JSObject {
|
||||
MutableHandleValue vp, JSBool strict);
|
||||
static JSBool setElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
MutableHandleValue vp, JSBool strict);
|
||||
static JSBool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
|
||||
uint32_t index, MutableHandleValue vp, bool *present);
|
||||
static JSBool setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
|
||||
MutableHandleValue vp, JSBool strict);
|
||||
static JSBool getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
|
||||
|
502
js/src/builtin/Profilers.cpp
Normal file
502
js/src/builtin/Profilers.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/* Profiling-related API */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "Profilers.h"
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsprobes.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
#include <valgrind/callgrind.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "devtools/sharkctl.h"
|
||||
#endif
|
||||
|
||||
using namespace js;
|
||||
|
||||
/* Thread-unsafe error management */
|
||||
|
||||
static char gLastError[2000];
|
||||
|
||||
static void
|
||||
#ifdef __GNUC__
|
||||
__attribute__((unused,format(printf,1,2)))
|
||||
#endif
|
||||
UnsafeError(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
(void) vsnprintf(gLastError, sizeof(gLastError), format, args);
|
||||
va_end(args);
|
||||
|
||||
gLastError[sizeof(gLastError) - 1] = '\0';
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(const char *)
|
||||
JS_UnsafeGetLastProfilingError()
|
||||
{
|
||||
return gLastError;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StartProfiling(const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
if (!Shark::Start()) {
|
||||
UnsafeError("Failed to start Shark for %s", profileName);
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
if (!js_StartPerf())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StopProfiling(const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
Shark::Stop();
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
if (!js_StopPerf())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start or stop whatever platform- and configuration-specific profiling
|
||||
* backends are available.
|
||||
*/
|
||||
static JSBool
|
||||
ControlProfilers(bool toState)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
|
||||
if (! Probes::ProfilingActive && toState) {
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
if (!Shark::Start()) {
|
||||
UnsafeError("Failed to start Shark");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
if (! js_StartCallgrind()) {
|
||||
UnsafeError("Failed to start Callgrind");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
} else if (Probes::ProfilingActive && ! toState) {
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
Shark::Stop();
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
if (! js_StopCallgrind()) {
|
||||
UnsafeError("failed to stop Callgrind");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Probes::ProfilingActive = toState;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pause/resume whatever profiling mechanism is currently compiled
|
||||
* in, if applicable. This will not affect things like dtrace.
|
||||
*
|
||||
* Do not mix calls to these APIs with calls to the individual
|
||||
* profilers' pause/resume functions, because only overall state is
|
||||
* tracked, not the state of each profiler.
|
||||
*/
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_PauseProfilers(const char *profileName)
|
||||
{
|
||||
return ControlProfilers(false);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_ResumeProfilers(const char *profileName)
|
||||
{
|
||||
return ControlProfilers(true);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DumpProfile(const char *outfile, const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#ifdef MOZ_CALLGRIND
|
||||
js_DumpCallgrind(outfile);
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
#ifdef MOZ_PROFILING
|
||||
|
||||
struct RequiredStringArg {
|
||||
JSContext *mCx;
|
||||
char *mBytes;
|
||||
RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
|
||||
: mCx(cx), mBytes(NULL)
|
||||
{
|
||||
if (argc <= argi) {
|
||||
JS_ReportError(cx, "%s: not enough arguments", caller);
|
||||
} else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
|
||||
JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
|
||||
} else {
|
||||
mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
|
||||
}
|
||||
}
|
||||
operator void*() {
|
||||
return (void*) mBytes;
|
||||
}
|
||||
~RequiredStringArg() {
|
||||
if (mBytes)
|
||||
js_free(mBytes);
|
||||
}
|
||||
};
|
||||
|
||||
static JSBool
|
||||
StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Usage: DumpProfile([filename[, profileName]]) */
|
||||
static JSBool
|
||||
DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
bool ret;
|
||||
if (argc == 0) {
|
||||
ret = JS_DumpProfile(NULL, NULL);
|
||||
} else {
|
||||
RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
|
||||
if (!filename)
|
||||
return JS_FALSE;
|
||||
|
||||
if (argc == 1) {
|
||||
ret = JS_DumpProfile(filename.mBytes, NULL);
|
||||
} else {
|
||||
RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
|
||||
ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
|
||||
}
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_SHARK
|
||||
|
||||
static JSBool
|
||||
IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, JSVAL_TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
static JSBool
|
||||
StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
|
||||
if (!outFile)
|
||||
return JS_FALSE;
|
||||
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSFunctionSpec profiling_functions[] = {
|
||||
JS_FN("startProfiling", StartProfiling, 1,0),
|
||||
JS_FN("stopProfiling", StopProfiling, 1,0),
|
||||
JS_FN("pauseProfilers", PauseProfilers, 1,0),
|
||||
JS_FN("resumeProfilers", ResumeProfilers, 1,0),
|
||||
JS_FN("dumpProfile", DumpProfile, 2,0),
|
||||
#ifdef MOZ_SHARK
|
||||
/* Keep users of the old shark API happy. */
|
||||
JS_FN("connectShark", IgnoreAndReturnTrue, 0,0),
|
||||
JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
|
||||
JS_FN("startShark", StartProfiling, 0,0),
|
||||
JS_FN("stopShark", StopProfiling, 0,0),
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
JS_FN("startCallgrind", StartCallgrind, 0,0),
|
||||
JS_FN("stopCallgrind", StopCallgrind, 0,0),
|
||||
JS_FN("dumpCallgrind", DumpCallgrind, 1,0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
|
||||
assertSameCompartment(cx, obj);
|
||||
#ifdef MOZ_PROFILING
|
||||
return JS_DefineFunctions(cx, obj, profiling_functions);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_StartCallgrind()
|
||||
{
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION);
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_StopCallgrind()
|
||||
{
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_DumpCallgrind(const char *outfile)
|
||||
{
|
||||
if (outfile) {
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile));
|
||||
} else {
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* MOZ_CALLGRIND */
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
/*
|
||||
* Code for starting and stopping |perf|, the Linux profiler.
|
||||
*
|
||||
* Output from profiling is written to mozperf.data in your cwd.
|
||||
*
|
||||
* To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment.
|
||||
*
|
||||
* To pass additional parameters to |perf record|, provide them in the
|
||||
* MOZ_PROFILE_PERF_FLAGS environment variable. If this variable does not
|
||||
* exist, we default it to "--call-graph". (If you don't want --call-graph but
|
||||
* don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty
|
||||
* string.)
|
||||
*
|
||||
* If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just
|
||||
* asking for trouble.
|
||||
*
|
||||
* Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to
|
||||
* work if you pass an argument which includes a space (e.g.
|
||||
* MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'").
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
static bool perfInitialized = false;
|
||||
static pid_t perfPid = 0;
|
||||
|
||||
JSBool js_StartPerf()
|
||||
{
|
||||
const char *outfile = "mozperf.data";
|
||||
|
||||
if (perfPid != 0) {
|
||||
UnsafeError("js_StartPerf: called while perf was already running!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail if MOZ_PROFILE_WITH_PERF is empty or undefined.
|
||||
if (!getenv("MOZ_PROFILE_WITH_PERF") ||
|
||||
!strlen(getenv("MOZ_PROFILE_WITH_PERF"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete mozperf.data the first time through -- we're going to append to it
|
||||
* later on, so we want it to be clean when we start out.
|
||||
*/
|
||||
if (!perfInitialized) {
|
||||
perfInitialized = true;
|
||||
unlink(outfile);
|
||||
char cwd[4096];
|
||||
printf("Writing perf profiling data to %s/%s\n",
|
||||
getcwd(cwd, sizeof(cwd)), outfile);
|
||||
}
|
||||
|
||||
pid_t mainPid = getpid();
|
||||
|
||||
pid_t childPid = fork();
|
||||
if (childPid == 0) {
|
||||
/* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */
|
||||
|
||||
char mainPidStr[16];
|
||||
snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid);
|
||||
const char *defaultArgs[] = {"perf", "record", "--append",
|
||||
"--pid", mainPidStr, "--output", outfile};
|
||||
|
||||
Vector<const char*, 0, SystemAllocPolicy> args;
|
||||
args.append(defaultArgs, ArrayLength(defaultArgs));
|
||||
|
||||
const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS");
|
||||
if (!flags) {
|
||||
flags = "--call-graph";
|
||||
}
|
||||
|
||||
// Split |flags| on spaces. (Don't bother to free it -- we're going to
|
||||
// exec anyway.)
|
||||
char *toksave;
|
||||
char *tok = strtok_r(strdup(flags), " ", &toksave);
|
||||
while (tok) {
|
||||
args.append(tok);
|
||||
tok = strtok_r(NULL, " ", &toksave);
|
||||
}
|
||||
|
||||
args.append((char*) NULL);
|
||||
|
||||
execvp("perf", const_cast<char**>(args.begin()));
|
||||
|
||||
/* Reached only if execlp fails. */
|
||||
fprintf(stderr, "Unable to start perf.\n");
|
||||
exit(1);
|
||||
}
|
||||
else if (childPid > 0) {
|
||||
perfPid = childPid;
|
||||
|
||||
/* Give perf a chance to warm up. */
|
||||
usleep(500 * 1000);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
UnsafeError("js_StartPerf: fork() failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSBool js_StopPerf()
|
||||
{
|
||||
if (perfPid == 0) {
|
||||
UnsafeError("js_StopPerf: perf is not running.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (kill(perfPid, SIGINT)) {
|
||||
UnsafeError("js_StopPerf: kill failed\n");
|
||||
|
||||
// Try to reap the process anyway.
|
||||
waitpid(perfPid, NULL, WNOHANG);
|
||||
}
|
||||
else {
|
||||
waitpid(perfPid, NULL, 0);
|
||||
}
|
||||
|
||||
perfPid = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
90
js/src/builtin/Profilers.h
Normal file
90
js/src/builtin/Profilers.h
Normal file
@ -0,0 +1,90 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99:
|
||||
*
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* Functions for controlling profilers from within JS: Valgrind, Perf,
|
||||
* Shark, etc.
|
||||
*/
|
||||
#ifndef Profilers_h___
|
||||
#define Profilers_h___
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
/**
|
||||
* Start any profilers that are available and have been configured on for this
|
||||
* platform. This is NOT thread safe.
|
||||
*
|
||||
* The profileName is used by some profilers to describe the current profiling
|
||||
* run. It may be used for part of the filename of the output, but the
|
||||
* specifics depend on the profiler. Many profilers will ignore it. Passing in
|
||||
* NULL is legal; some profilers may use it to output to stdout or similar.
|
||||
*
|
||||
* Returns true if no profilers fail to start.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_StartProfiling(const char *profileName);
|
||||
|
||||
/**
|
||||
* Stop any profilers that were previously started with JS_StartProfiling.
|
||||
* Returns true if no profilers fail to stop.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_StopProfiling(const char *profileName);
|
||||
|
||||
/**
|
||||
* Write the current profile data to the given file, if applicable to whatever
|
||||
* profiler is being used.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_DumpProfile(const char *outfile, const char *profileName);
|
||||
|
||||
/**
|
||||
* Pause currently active profilers (only supported by some profilers). Returns
|
||||
* whether any profilers failed to pause. (Profilers that do not support
|
||||
* pause/resume do not count.)
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_PauseProfilers(const char *profileName);
|
||||
|
||||
/**
|
||||
* Resume suspended profilers
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_ResumeProfilers(const char *profileName);
|
||||
|
||||
/**
|
||||
* The profiling API calls are not able to report errors, so they use a
|
||||
* thread-unsafe global memory buffer to hold the last error encountered. This
|
||||
* should only be called after something returns false.
|
||||
*/
|
||||
JS_PUBLIC_API(const char *)
|
||||
JS_UnsafeGetLastProfilingError();
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StopCallgrind();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StartCallgrind();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_DumpCallgrind(const char *outfile);
|
||||
|
||||
#endif /* MOZ_CALLGRIND */
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StartPerf();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StopPerf();
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
#endif /* Profilers_h___ */
|
@ -1047,7 +1047,7 @@ TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osr
|
||||
{
|
||||
if (!IonCompile<TestCompiler>(cx, script, fun, osrPc, constructing)) {
|
||||
if (!cx->isExceptionPending())
|
||||
ForbidCompilation(script);
|
||||
ForbidCompilation(cx, script);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1202,7 +1202,7 @@ ion::CanEnterAtBranch(JSContext *cx, HandleScript script, StackFrame *fp, jsbyte
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(fp)) {
|
||||
ForbidCompilation(script);
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
@ -1211,7 +1211,7 @@ ion::CanEnterAtBranch(JSContext *cx, HandleScript script, StackFrame *fp, jsbyte
|
||||
MethodStatus status = Compile<TestCompiler>(cx, script, fun, pc, false);
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(script);
|
||||
ForbidCompilation(cx, script);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1258,7 +1258,7 @@ ion::CanEnter(JSContext *cx, HandleScript script, StackFrame *fp, bool newType)
|
||||
|
||||
// Mark as forbidden if frame can't be handled.
|
||||
if (!CheckFrame(fp)) {
|
||||
ForbidCompilation(script);
|
||||
ForbidCompilation(cx, script);
|
||||
return Method_CantCompile;
|
||||
}
|
||||
|
||||
@ -1267,7 +1267,7 @@ ion::CanEnter(JSContext *cx, HandleScript script, StackFrame *fp, bool newType)
|
||||
MethodStatus status = Compile<TestCompiler>(cx, script, fun, NULL, fp->isConstructing());
|
||||
if (status != Method_Compiled) {
|
||||
if (status == Method_CantCompile)
|
||||
ForbidCompilation(script);
|
||||
ForbidCompilation(cx, script);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1634,7 +1634,9 @@ ion::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
|
||||
JS_ASSERT(script->hasIonScript());
|
||||
|
||||
Vector<types::RecompileInfo> scripts(cx);
|
||||
scripts.append(script->ionScript()->recompileInfo());
|
||||
if (!scripts.append(script->ionScript()->recompileInfo()))
|
||||
return false;
|
||||
|
||||
Invalidate(cx, scripts, resetUses);
|
||||
return true;
|
||||
}
|
||||
@ -1667,17 +1669,19 @@ ion::MarkFromIon(JSCompartment *comp, Value *vp)
|
||||
}
|
||||
|
||||
void
|
||||
ion::ForbidCompilation(JSScript *script)
|
||||
ion::ForbidCompilation(JSContext *cx, JSScript *script)
|
||||
{
|
||||
IonSpew(IonSpew_Abort, "Disabling Ion compilation of script %s:%d",
|
||||
script->filename, script->lineno);
|
||||
|
||||
if (script->hasIonScript() && script->compartment()->needsBarrier()) {
|
||||
// We're about to remove edges from the JSScript to gcthings
|
||||
// embedded in the IonScript. Perform one final trace of the
|
||||
// IonScript for the incremental GC, as it must know about
|
||||
// those edges.
|
||||
IonScript::Trace(script->compartment()->barrierTracer(), script->ion);
|
||||
if (script->hasIonScript()) {
|
||||
// It is only safe to modify script->ion if the script is not currently
|
||||
// running, because IonFrameIterator needs to tell what ionScript to
|
||||
// use (either the one on the JSScript, or the one hidden in the
|
||||
// breadcrumbs Invalidation() leaves). Therefore, if invalidation
|
||||
// fails, we cannot disable the script.
|
||||
if (!Invalidate(cx, script, false))
|
||||
return;
|
||||
}
|
||||
|
||||
script->ion = ION_DISABLED_SCRIPT;
|
||||
|
@ -252,7 +252,7 @@ static inline bool IsEnabled(JSContext *cx)
|
||||
return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
|
||||
}
|
||||
|
||||
void ForbidCompilation(JSScript *script);
|
||||
void ForbidCompilation(JSContext *cx, JSScript *script);
|
||||
uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
|
||||
|
||||
} // namespace ion
|
||||
|
@ -39,14 +39,13 @@ InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *
|
||||
JSScript *script = GetTopIonJSScript(cx);
|
||||
if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
|
||||
AutoFlushCache afc("InvokeFunction");
|
||||
Invalidate(cx, script, false);
|
||||
|
||||
// Finally, poison the script so we don't try to run it again
|
||||
ForbidCompilation(script);
|
||||
// Poison the script so we don't try to run it again. This will
|
||||
// trigger invalidation.
|
||||
ForbidCompilation(cx, script);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TI will return false for monitorReturnTypes, meaning there is no
|
||||
// TypeBarrier or Monitor instruction following this. However, we need to
|
||||
// explicitly monitor if the callee has not been analyzed yet. We special
|
||||
|
11
js/src/jit-test/tests/basic/evaluate-restore-options.js
Normal file
11
js/src/jit-test/tests/basic/evaluate-restore-options.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Bug 791157: the shell 'evaluate' function should properly restore the
|
||||
// context's options.
|
||||
|
||||
try {
|
||||
evaluate('%', {noScriptRval: true});
|
||||
} catch(e) {}
|
||||
new Function("");
|
||||
|
||||
try {
|
||||
evaluate('new Function("");', {noScriptRval: true});
|
||||
} catch (e) {}
|
@ -1,11 +1,9 @@
|
||||
function testLength() {
|
||||
// Test length attributes
|
||||
// Test length immutability.
|
||||
var p = new ParallelArray([1,2,3,4]);
|
||||
var desc = Object.getOwnPropertyDescriptor(p, "length");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(desc.writable, false);
|
||||
assertEq(desc.value, 4);
|
||||
p.length = 0;
|
||||
assertEq(p[0], 1);
|
||||
assertEq(p.length, 4);
|
||||
}
|
||||
|
||||
testLength();
|
||||
|
@ -7,9 +7,8 @@ function testShape() {
|
||||
shape.push(i+1);
|
||||
var p = new ParallelArray(shape, function () { return 0; });
|
||||
// Test shape identity and shape
|
||||
assertEq(p.shape, p.shape);
|
||||
assertEq(p.shape !== shape, true);
|
||||
assertEqArray(p.shape, shape);
|
||||
assertEq(p.shape !== shape, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
12
js/src/jit-test/tests/parallelarray/shape-4.js
Normal file
12
js/src/jit-test/tests/parallelarray/shape-4.js
Normal file
@ -0,0 +1,12 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
function testShape() {
|
||||
// Test shape immutability.
|
||||
var p = new ParallelArray([1,2,3,4]);
|
||||
p.shape[0] = 0;
|
||||
p.shape[1] = 42;
|
||||
assertEq(p[0], 1);
|
||||
assertEqArray(p.shape, [4]);
|
||||
}
|
||||
|
||||
testShape();
|
@ -1,3 +1,5 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
// ParallelArray surfaces
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(this, "ParallelArray");
|
||||
@ -35,3 +37,14 @@ checkMethod("filter", 1);
|
||||
checkMethod("flatten", 0);
|
||||
checkMethod("partition", 1);
|
||||
checkMethod("get", 1);
|
||||
|
||||
function checkAccessor(name) {
|
||||
var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq(typeof desc.get, 'function');
|
||||
assertEq(desc.set, undefined);
|
||||
}
|
||||
|
||||
checkAccessor("length");
|
||||
checkAccessor("shape");
|
||||
|
3
js/src/jit-test/tests/parallelarray/surfaces-3.js
Normal file
3
js/src/jit-test/tests/parallelarray/surfaces-3.js
Normal file
@ -0,0 +1,3 @@
|
||||
// ParallelArray objects are frozen.
|
||||
|
||||
assertEq(Object.isFrozen(new ParallelArray), true);
|
@ -354,25 +354,24 @@ MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "paramet
|
||||
MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression")
|
||||
MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}")
|
||||
MSG_DEF(JSMSG_ALREADY_HAS_SOURCEMAP, 303, 1, JSEXN_ERR, "{0} is being assigned a source map, yet already has one")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_IMMUTABLE, 304, 0, JSEXN_TYPEERR, "ParallelArray objects are immutable")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 305, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 306, 0, JSEXN_ERR, "argument must be divisible by outermost dimension")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 307, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 308, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 309, 0, JSEXN_ERR, "no conflict resolution function provided")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 310, 0, JSEXN_ERR, "index in scatter vector out of bounds")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 311, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 312, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NEW, 313, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 314, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 315, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NEW, 316, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 317, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 318, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_INVALID_TRAP_RESULT, 319, 2, JSEXN_TYPEERR, "trap {1} for {0} returned an invalid result")
|
||||
MSG_DEF(JSMSG_CANT_SKIP_NC, 320, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 321, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 322, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
|
||||
MSG_DEF(JSMSG_CANT_SET_NW_NC, 323, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 324, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 325, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
|
||||
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 311, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NEW, 312, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 313, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 314, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NEW, 315, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 316, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
|
||||
MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 317, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
|
||||
MSG_DEF(JSMSG_INVALID_TRAP_RESULT, 318, 2, JSEXN_TYPEERR, "trap {1} for {0} returned an invalid result")
|
||||
MSG_DEF(JSMSG_CANT_SKIP_NC, 319, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 320, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 321, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
|
||||
MSG_DEF(JSMSG_CANT_SET_NW_NC, 322, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
|
||||
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
|
||||
MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
|
||||
|
@ -449,13 +449,11 @@ JSStructuredCloneWriter::writeArrayBuffer(JSHandleObject obj)
|
||||
}
|
||||
|
||||
bool
|
||||
JSStructuredCloneWriter::startObject(JSHandleObject obj)
|
||||
JSStructuredCloneWriter::startObject(JSHandleObject obj, bool *backref)
|
||||
{
|
||||
JS_ASSERT(obj->isArray() || obj->isObject());
|
||||
|
||||
/* Handle cycles in the object graph. */
|
||||
CloneMemory::AddPtr p = memory.lookupForAdd(obj);
|
||||
if (p)
|
||||
if ((*backref = p))
|
||||
return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value);
|
||||
if (!memory.add(p, obj, memory.count()))
|
||||
return false;
|
||||
@ -466,6 +464,12 @@ JSStructuredCloneWriter::startObject(JSHandleObject obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JSStructuredCloneWriter::traverseObject(JSHandleObject obj)
|
||||
{
|
||||
/*
|
||||
* Get enumerable property ids and put them in reverse order so that they
|
||||
* will come off the stack in forward order.
|
||||
@ -511,6 +515,13 @@ JSStructuredCloneWriter::startWrite(const Value &v)
|
||||
return false;
|
||||
|
||||
AutoCompartment ac(context(), obj);
|
||||
|
||||
bool backref;
|
||||
if (!startObject(obj, &backref))
|
||||
return false;
|
||||
if (backref)
|
||||
return true;
|
||||
|
||||
if (obj->isRegExp()) {
|
||||
RegExpObject &reobj = obj->asRegExp();
|
||||
return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
|
||||
@ -518,12 +529,12 @@ JSStructuredCloneWriter::startWrite(const Value &v)
|
||||
} else if (obj->isDate()) {
|
||||
double d = js_DateGetMsecSinceEpoch(context(), obj);
|
||||
return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
|
||||
} else if (obj->isObject() || obj->isArray()) {
|
||||
return startObject(obj);
|
||||
} else if (obj->isTypedArray()) {
|
||||
return writeTypedArray(obj);
|
||||
} else if (obj->isArrayBuffer() && obj->asArrayBuffer().hasData()) {
|
||||
return writeArrayBuffer(obj);
|
||||
} else if (obj->isObject() || obj->isArray()) {
|
||||
return traverseObject(obj);
|
||||
} else if (obj->isBoolean()) {
|
||||
return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->asBoolean().unbox());
|
||||
} else if (obj->isNumber()) {
|
||||
@ -821,8 +832,7 @@ JSStructuredCloneReader::startRead(Value *vp)
|
||||
JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
|
||||
? NewDenseEmptyArray(context())
|
||||
: NewBuiltinClassInstance(context(), &ObjectClass);
|
||||
if (!obj || !objs.append(ObjectValue(*obj)) ||
|
||||
!allObjs.append(ObjectValue(*obj)))
|
||||
if (!obj || !objs.append(ObjectValue(*obj)))
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
break;
|
||||
@ -832,14 +842,17 @@ JSStructuredCloneReader::startRead(Value *vp)
|
||||
if (data >= allObjs.length()) {
|
||||
JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
|
||||
JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
"invalid input");
|
||||
"invalid back reference in input");
|
||||
return false;
|
||||
}
|
||||
*vp = allObjs[data];
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case SCTAG_ARRAY_BUFFER_OBJECT:
|
||||
return readArrayBuffer(data, vp);
|
||||
if (!readArrayBuffer(data, vp))
|
||||
return false;
|
||||
break;
|
||||
|
||||
default: {
|
||||
if (tag <= SCTAG_FLOAT_MAX) {
|
||||
@ -850,8 +863,11 @@ JSStructuredCloneReader::startRead(Value *vp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX)
|
||||
return readTypedArray(tag, data, vp);
|
||||
if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX) {
|
||||
if (!readTypedArray(tag, data, vp))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!callbacks || !callbacks->read) {
|
||||
JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
@ -864,6 +880,10 @@ JSStructuredCloneReader::startRead(Value *vp)
|
||||
vp->setObject(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (vp->isObject() && !allObjs.append(*vp))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -134,8 +134,9 @@ struct JSStructuredCloneWriter {
|
||||
bool writeId(jsid id);
|
||||
bool writeArrayBuffer(JSHandleObject obj);
|
||||
bool writeTypedArray(JSHandleObject obj);
|
||||
bool startObject(JSHandleObject obj);
|
||||
bool startObject(JSHandleObject obj, bool *backref);
|
||||
bool startWrite(const js::Value &v);
|
||||
bool traverseObject(JSHandleObject obj);
|
||||
|
||||
inline void checkStack();
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
* JS debugging API.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "jsprvtd.h"
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
@ -48,10 +47,6 @@
|
||||
#include "jsautooplen.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "devtools/sharkctl.h"
|
||||
#endif
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using namespace mozilla;
|
||||
@ -1064,652 +1059,6 @@ JS_GetGlobalDebugHooks(JSRuntime *rt)
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/* Profiling-related API */
|
||||
|
||||
/* Thread-unsafe error management */
|
||||
|
||||
static char gLastError[2000];
|
||||
|
||||
static void
|
||||
#ifdef __GNUC__
|
||||
__attribute__((unused,format(printf,1,2)))
|
||||
#endif
|
||||
UnsafeError(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
(void) vsnprintf(gLastError, sizeof(gLastError), format, args);
|
||||
va_end(args);
|
||||
|
||||
gLastError[sizeof(gLastError) - 1] = '\0';
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(const char *)
|
||||
JS_UnsafeGetLastProfilingError()
|
||||
{
|
||||
return gLastError;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StartProfiling(const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
if (!Shark::Start()) {
|
||||
UnsafeError("Failed to start Shark for %s", profileName);
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#if 0 //def MOZ_VTUNE
|
||||
if (!js_StartVtune(profileName))
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
if (!js_StartPerf())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StopProfiling(const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
Shark::Stop();
|
||||
#endif
|
||||
#if 0 //def MOZ_VTUNE
|
||||
if (!js_StopVtune())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
if (!js_StopPerf())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start or stop whatever platform- and configuration-specific profiling
|
||||
* backends are available.
|
||||
*/
|
||||
static JSBool
|
||||
ControlProfilers(bool toState)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
|
||||
if (! Probes::ProfilingActive && toState) {
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
if (!Shark::Start()) {
|
||||
UnsafeError("Failed to start Shark");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
if (! js_StartCallgrind()) {
|
||||
UnsafeError("Failed to start Callgrind");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#if 0 //def MOZ_VTUNE
|
||||
if (! js_ResumeVtune())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
} else if (Probes::ProfilingActive && ! toState) {
|
||||
#if defined(MOZ_SHARK) && defined(__APPLE__)
|
||||
Shark::Stop();
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
if (! js_StopCallgrind()) {
|
||||
UnsafeError("failed to stop Callgrind");
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
#endif
|
||||
#if 0 //def MOZ_VTUNE
|
||||
if (! js_PauseVtune())
|
||||
ok = JS_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
Probes::ProfilingActive = toState;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pause/resume whatever profiling mechanism is currently compiled
|
||||
* in, if applicable. This will not affect things like dtrace.
|
||||
*
|
||||
* Do not mix calls to these APIs with calls to the individual
|
||||
* profilers' pause/resume functions, because only overall state is
|
||||
* tracked, not the state of each profiler.
|
||||
*/
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_PauseProfilers(const char *profileName)
|
||||
{
|
||||
return ControlProfilers(false);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_ResumeProfilers(const char *profileName)
|
||||
{
|
||||
return ControlProfilers(true);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DumpProfile(const char *outfile, const char *profileName)
|
||||
{
|
||||
JSBool ok = JS_TRUE;
|
||||
#ifdef MOZ_CALLGRIND
|
||||
js_DumpCallgrind(outfile);
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
#ifdef MOZ_PROFILING
|
||||
|
||||
struct RequiredStringArg {
|
||||
JSContext *mCx;
|
||||
char *mBytes;
|
||||
RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
|
||||
: mCx(cx), mBytes(NULL)
|
||||
{
|
||||
if (argc <= argi) {
|
||||
JS_ReportError(cx, "%s: not enough arguments", caller);
|
||||
} else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
|
||||
JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
|
||||
} else {
|
||||
mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
|
||||
}
|
||||
}
|
||||
operator void*() {
|
||||
return (void*) mBytes;
|
||||
}
|
||||
~RequiredStringArg() {
|
||||
if (mBytes)
|
||||
js_free(mBytes);
|
||||
}
|
||||
};
|
||||
|
||||
static JSBool
|
||||
StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Usage: DumpProfile([filename[, profileName]]) */
|
||||
static JSBool
|
||||
DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
bool ret;
|
||||
if (argc == 0) {
|
||||
ret = JS_DumpProfile(NULL, NULL);
|
||||
} else {
|
||||
RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
|
||||
if (!filename)
|
||||
return JS_FALSE;
|
||||
|
||||
if (argc == 1) {
|
||||
ret = JS_DumpProfile(filename.mBytes, NULL);
|
||||
} else {
|
||||
RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
|
||||
ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
|
||||
}
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_SHARK
|
||||
|
||||
static JSBool
|
||||
IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, JSVAL_TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
static JSBool
|
||||
StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
if (argc == 0) {
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
|
||||
if (!outFile)
|
||||
return JS_FALSE;
|
||||
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_VTUNE
|
||||
static JSBool
|
||||
StartVtune(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
RequiredStringArg profileName(cx, argc, vp, 0, "startVtune");
|
||||
if (!profileName)
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes)));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
StopVtune(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PauseVtune(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ResumeVtune(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune()));
|
||||
return JS_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSFunctionSpec profiling_functions[] = {
|
||||
JS_FN("startProfiling", StartProfiling, 1,0),
|
||||
JS_FN("stopProfiling", StopProfiling, 1,0),
|
||||
JS_FN("pauseProfilers", PauseProfilers, 1,0),
|
||||
JS_FN("resumeProfilers", ResumeProfilers, 1,0),
|
||||
JS_FN("dumpProfile", DumpProfile, 2,0),
|
||||
#ifdef MOZ_SHARK
|
||||
/* Keep users of the old shark API happy. */
|
||||
JS_FN("connectShark", IgnoreAndReturnTrue, 0,0),
|
||||
JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
|
||||
JS_FN("startShark", StartProfiling, 0,0),
|
||||
JS_FN("stopShark", StopProfiling, 0,0),
|
||||
#endif
|
||||
#ifdef MOZ_CALLGRIND
|
||||
JS_FN("startCallgrind", StartCallgrind, 0,0),
|
||||
JS_FN("stopCallgrind", StopCallgrind, 0,0),
|
||||
JS_FN("dumpCallgrind", DumpCallgrind, 1,0),
|
||||
#endif
|
||||
#if 0 //ef MOZ_VTUNE
|
||||
JS_FN("startVtune", js_StartVtune, 1,0),
|
||||
JS_FN("stopVtune", js_StopVtune, 0,0),
|
||||
JS_FN("pauseVtune", js_PauseVtune, 0,0),
|
||||
JS_FN("resumeVtune", js_ResumeVtune, 0,0),
|
||||
#endif
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
|
||||
assertSameCompartment(cx, obj);
|
||||
#ifdef MOZ_PROFILING
|
||||
return JS_DefineFunctions(cx, obj, profiling_functions);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
|
||||
#include <valgrind/callgrind.h>
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_StartCallgrind()
|
||||
{
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION);
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_StopCallgrind()
|
||||
{
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_DumpCallgrind(const char *outfile)
|
||||
{
|
||||
if (outfile) {
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile));
|
||||
} else {
|
||||
JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* MOZ_CALLGRIND */
|
||||
|
||||
#if 0 //def MOZ_VTUNE
|
||||
#include <VTuneApi.h>
|
||||
|
||||
static const char *vtuneErrorMessages[] = {
|
||||
"unknown, error #0",
|
||||
"invalid 'max samples' field",
|
||||
"invalid 'samples per buffer' field",
|
||||
"invalid 'sample interval' field",
|
||||
"invalid path",
|
||||
"sample file in use",
|
||||
"invalid 'number of events' field",
|
||||
"unknown, error #7",
|
||||
"internal error",
|
||||
"bad event name",
|
||||
"VTStopSampling called without calling VTStartSampling",
|
||||
"no events selected for event-based sampling",
|
||||
"events selected cannot be run together",
|
||||
"no sampling parameters",
|
||||
"sample database already exists",
|
||||
"sampling already started",
|
||||
"time-based sampling not supported",
|
||||
"invalid 'sampling parameters size' field",
|
||||
"invalid 'event size' field",
|
||||
"sampling file already bound",
|
||||
"invalid event path",
|
||||
"invalid license",
|
||||
"invalid 'global options' field",
|
||||
|
||||
};
|
||||
|
||||
bool
|
||||
js_StartVtune(const char *profileName)
|
||||
{
|
||||
VTUNE_EVENT events[] = {
|
||||
{ 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
|
||||
{ 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
|
||||
};
|
||||
|
||||
U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
|
||||
char *default_filename = "mozilla-vtune.tb5";
|
||||
JSString *str;
|
||||
U32 status;
|
||||
|
||||
VTUNE_SAMPLING_PARAMS params = {
|
||||
sizeof(VTUNE_SAMPLING_PARAMS),
|
||||
sizeof(VTUNE_EVENT),
|
||||
0, 0, /* Reserved fields */
|
||||
1, /* Initialize in "paused" state */
|
||||
0, /* Max samples, or 0 for "continuous" */
|
||||
4096, /* Samples per buffer */
|
||||
0.1, /* Sampling interval in ms */
|
||||
1, /* 1 for event-based sampling, 0 for time-based */
|
||||
|
||||
n_events,
|
||||
events,
|
||||
default_filename,
|
||||
};
|
||||
|
||||
if (profileName) {
|
||||
char filename[strlen(profileName) + strlen("-vtune.tb5") + 1];
|
||||
snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName);
|
||||
params.tb5Filename = filename;
|
||||
}
|
||||
|
||||
status = VTStartSampling(¶ms);
|
||||
|
||||
if (params.tb5Filename != default_filename)
|
||||
js_free(params.tb5Filename);
|
||||
|
||||
if (status != 0) {
|
||||
if (status == VTAPI_MULTIPLE_RUNS)
|
||||
VTStopSampling(0);
|
||||
if (status < sizeof(vtuneErrorMessages))
|
||||
UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]);
|
||||
else
|
||||
UnsafeError("Vtune setup error: %d", status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js_StopVtune()
|
||||
{
|
||||
U32 status = VTStopSampling(1);
|
||||
if (status) {
|
||||
if (status < sizeof(vtuneErrorMessages))
|
||||
UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]);
|
||||
else
|
||||
UnsafeError("Vtune shutdown error: %d", status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js_PauseVtune()
|
||||
{
|
||||
VTPause();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js_ResumeVtune()
|
||||
{
|
||||
VTResume();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* MOZ_VTUNE */
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
/*
|
||||
* Code for starting and stopping |perf|, the Linux profiler.
|
||||
*
|
||||
* Output from profiling is written to mozperf.data in your cwd.
|
||||
*
|
||||
* To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment.
|
||||
*
|
||||
* To pass additional parameters to |perf record|, provide them in the
|
||||
* MOZ_PROFILE_PERF_FLAGS environment variable. If this variable does not
|
||||
* exist, we default it to "--call-graph". (If you don't want --call-graph but
|
||||
* don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty
|
||||
* string.)
|
||||
*
|
||||
* If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just
|
||||
* asking for trouble.
|
||||
*
|
||||
* Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to
|
||||
* work if you pass an argument which includes a space (e.g.
|
||||
* MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'").
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
static bool perfInitialized = false;
|
||||
static pid_t perfPid = 0;
|
||||
|
||||
JSBool js_StartPerf()
|
||||
{
|
||||
const char *outfile = "mozperf.data";
|
||||
|
||||
if (perfPid != 0) {
|
||||
UnsafeError("js_StartPerf: called while perf was already running!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail if MOZ_PROFILE_WITH_PERF is empty or undefined.
|
||||
if (!getenv("MOZ_PROFILE_WITH_PERF") ||
|
||||
!strlen(getenv("MOZ_PROFILE_WITH_PERF"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete mozperf.data the first time through -- we're going to append to it
|
||||
* later on, so we want it to be clean when we start out.
|
||||
*/
|
||||
if (!perfInitialized) {
|
||||
perfInitialized = true;
|
||||
unlink(outfile);
|
||||
char cwd[4096];
|
||||
printf("Writing perf profiling data to %s/%s\n",
|
||||
getcwd(cwd, sizeof(cwd)), outfile);
|
||||
}
|
||||
|
||||
pid_t mainPid = getpid();
|
||||
|
||||
pid_t childPid = fork();
|
||||
if (childPid == 0) {
|
||||
/* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */
|
||||
|
||||
char mainPidStr[16];
|
||||
snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid);
|
||||
const char *defaultArgs[] = {"perf", "record", "--append",
|
||||
"--pid", mainPidStr, "--output", outfile};
|
||||
|
||||
Vector<const char*, 0, SystemAllocPolicy> args;
|
||||
args.append(defaultArgs, ArrayLength(defaultArgs));
|
||||
|
||||
const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS");
|
||||
if (!flags) {
|
||||
flags = "--call-graph";
|
||||
}
|
||||
|
||||
// Split |flags| on spaces. (Don't bother to free it -- we're going to
|
||||
// exec anyway.)
|
||||
char *toksave;
|
||||
char *tok = strtok_r(strdup(flags), " ", &toksave);
|
||||
while (tok) {
|
||||
args.append(tok);
|
||||
tok = strtok_r(NULL, " ", &toksave);
|
||||
}
|
||||
|
||||
args.append((char*) NULL);
|
||||
|
||||
execvp("perf", const_cast<char**>(args.begin()));
|
||||
|
||||
/* Reached only if execlp fails. */
|
||||
fprintf(stderr, "Unable to start perf.\n");
|
||||
exit(1);
|
||||
}
|
||||
else if (childPid > 0) {
|
||||
perfPid = childPid;
|
||||
|
||||
/* Give perf a chance to warm up. */
|
||||
usleep(500 * 1000);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
UnsafeError("js_StartPerf: fork() failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSBool js_StopPerf()
|
||||
{
|
||||
if (perfPid == 0) {
|
||||
UnsafeError("js_StopPerf: perf is not running.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (kill(perfPid, SIGINT)) {
|
||||
UnsafeError("js_StopPerf: kill failed\n");
|
||||
|
||||
// Try to reap the process anyway.
|
||||
waitpid(perfPid, NULL, WNOHANG);
|
||||
}
|
||||
else {
|
||||
waitpid(perfPid, NULL, 0);
|
||||
}
|
||||
|
||||
perfPid = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_DumpBytecode(JSContext *cx, JSScript *scriptArg)
|
||||
{
|
||||
|
@ -394,48 +394,6 @@ js_RevertVersion(JSContext *cx);
|
||||
extern JS_PUBLIC_API(const JSDebugHooks *)
|
||||
JS_GetGlobalDebugHooks(JSRuntime *rt);
|
||||
|
||||
/**
|
||||
* Start any profilers that are available and have been configured on for this
|
||||
* platform. This is NOT thread safe.
|
||||
*
|
||||
* The profileName is used by some profilers to describe the current profiling
|
||||
* run. It may be used for part of the filename of the output, but the
|
||||
* specifics depend on the profiler. Many profilers will ignore it. Passing in
|
||||
* NULL is legal; some profilers may use it to output to stdout or similar.
|
||||
*
|
||||
* Returns true if no profilers fail to start.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_StartProfiling(const char *profileName);
|
||||
|
||||
/**
|
||||
* Stop any profilers that were previously started with JS_StartProfiling.
|
||||
* Returns true if no profilers fail to stop.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_StopProfiling(const char *profileName);
|
||||
|
||||
/**
|
||||
* Write the current profile data to the given file, if applicable to whatever
|
||||
* profiler is being used.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_DumpProfile(const char *outfile, const char *profileName);
|
||||
|
||||
/**
|
||||
* Pause currently active profilers (only supported by some profilers). Returns
|
||||
* whether any profilers failed to pause. (Profilers that do not support
|
||||
* pause/resume do not count.)
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_PauseProfilers(const char *profileName);
|
||||
|
||||
/**
|
||||
* Resume suspended profilers
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_ResumeProfilers(const char *profileName);
|
||||
|
||||
/**
|
||||
* Add various profiling-related functions as properties of the given object.
|
||||
*/
|
||||
@ -446,53 +404,6 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_DefineDebuggerObject(JSContext *cx, JSObject *obj);
|
||||
|
||||
/**
|
||||
* The profiling API calls are not able to report errors, so they use a
|
||||
* thread-unsafe global memory buffer to hold the last error encountered. This
|
||||
* should only be called after something returns false.
|
||||
*/
|
||||
JS_PUBLIC_API(const char *)
|
||||
JS_UnsafeGetLastProfilingError();
|
||||
|
||||
#ifdef MOZ_CALLGRIND
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StopCallgrind();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StartCallgrind();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_DumpCallgrind(const char *outfile);
|
||||
|
||||
#endif /* MOZ_CALLGRIND */
|
||||
|
||||
#ifdef MOZ_VTUNE
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
js_StartVtune(const char *profileName);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
js_StopVtune();
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
js_PauseVtune();
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
js_ResumeVtune();
|
||||
|
||||
#endif /* MOZ_VTUNE */
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StartPerf();
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_StopPerf();
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_DumpBytecode(JSContext *cx, JSScript *script);
|
||||
|
||||
|
@ -131,7 +131,7 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValu
|
||||
// detecting its use early.
|
||||
JSScript *script = iter.script();
|
||||
if (!script->hasIonScript())
|
||||
ion::ForbidCompilation(script);
|
||||
ion::ForbidCompilation(cx, script);
|
||||
#endif
|
||||
|
||||
vp.setObject(*argsobj);
|
||||
|
@ -902,16 +902,17 @@ Evaluate(JSContext *cx, unsigned argc, jsval *vp)
|
||||
options |= JSOPTION_COMPILE_N_GO;
|
||||
if (noScriptRval)
|
||||
options |= JSOPTION_NO_SCRIPT_RVAL;
|
||||
JS_SetOptions(cx, options);
|
||||
|
||||
JS_SetOptions(cx, options);
|
||||
JSScript *script = JS_CompileUCScript(cx, global, codeChars, codeLength, fileName, lineNumber);
|
||||
JS_SetOptions(cx, saved);
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
if (sourceMapURL && !script->scriptSource()->hasSourceMap()) {
|
||||
if (!script->scriptSource()->setSourceMap(cx, sourceMapURL, script->filename))
|
||||
return false;
|
||||
}
|
||||
JS_SetOptions(cx, saved);
|
||||
if (!JS_ExecuteScript(cx, global, script, vp))
|
||||
return false;
|
||||
}
|
||||
|
@ -3,54 +3,144 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
// Set of properties on a cloned object that are legitimately non-enumerable,
|
||||
// grouped by object type.
|
||||
var non_enumerable = { 'Array': [ 'length' ],
|
||||
'String': [ 'length' ] };
|
||||
|
||||
// Set of properties on a cloned object that are legitimately non-configurable,
|
||||
// grouped by object type. The property name '0' stands in for any indexed
|
||||
// property.
|
||||
var non_configurable = { 'String': [ 0 ],
|
||||
'(typed array)': [ 0 ] };
|
||||
|
||||
// Set of properties on a cloned object that are legitimately non-writable,
|
||||
// grouped by object type. The property name '0' stands in for any indexed
|
||||
// property.
|
||||
var non_writable = { 'String': [ 0 ] };
|
||||
|
||||
function classOf(obj) {
|
||||
var classString = Object.prototype.toString.call(obj);
|
||||
var [ all, classname ] = classString.match(/\[object (\w+)/);
|
||||
return classname;
|
||||
}
|
||||
|
||||
function isIndex(p) {
|
||||
var u = p >>> 0;
|
||||
return ("" + u == p && u != 0xffffffff);
|
||||
}
|
||||
|
||||
function notIndex(p) {
|
||||
return !isIndex(p);
|
||||
}
|
||||
|
||||
function tableContains(table, cls, prop) {
|
||||
if (isIndex(prop))
|
||||
prop = 0;
|
||||
if (cls.match(/\wArray$/))
|
||||
cls = "(typed array)";
|
||||
var exceptionalProps = table[cls] || [];
|
||||
return exceptionalProps.indexOf(prop) != -1;
|
||||
}
|
||||
|
||||
function shouldBeConfigurable(cls, prop) {
|
||||
return !tableContains(non_configurable, cls, prop);
|
||||
}
|
||||
|
||||
function shouldBeWritable(cls, prop) {
|
||||
return !tableContains(non_writable, cls, prop);
|
||||
}
|
||||
|
||||
function ownProperties(obj) {
|
||||
return Object.getOwnPropertyNames(obj).
|
||||
map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; });
|
||||
}
|
||||
|
||||
function isCloneable(pair) {
|
||||
return typeof pair[0] === 'string' && pair[1].enumerable;
|
||||
}
|
||||
|
||||
function compareProperties(a, b, stack, path) {
|
||||
var ca = classOf(a);
|
||||
|
||||
// 'b', the original object, may have non-enumerable or XMLName properties;
|
||||
// ignore them. 'a', the clone, should not have any non-enumerable
|
||||
// properties (except .length, if it's an Array or String) or XMLName
|
||||
// properties.
|
||||
var pb = ownProperties(b).filter(isCloneable);
|
||||
var pa = ownProperties(a);
|
||||
for (var i = 0; i < pa.length; i++) {
|
||||
var propname = pa[i][0];
|
||||
assertEq(typeof propname, "string", "clone should not have E4X properties " + path);
|
||||
if (!pa[i][1].enumerable) {
|
||||
if (tableContains(non_enumerable, ca, propname)) {
|
||||
// remove it so that the comparisons below will work
|
||||
pa.splice(i, 1);
|
||||
i--;
|
||||
} else {
|
||||
throw new Error("non-enumerable clone property " + uneval(pa[i][0]) + " " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that, apart from properties whose names are array indexes,
|
||||
// the enumerable properties appear in the same order.
|
||||
var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
|
||||
var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
|
||||
assertEq(aNames.join(","), bNames.join(","), path);
|
||||
|
||||
// Check that the lists are the same when including array indexes.
|
||||
function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; }
|
||||
pa.sort(byName);
|
||||
pb.sort(byName);
|
||||
assertEq(pa.length, pb.length, "should see the same number of properties " + path);
|
||||
for (var i = 0; i < pa.length; i++) {
|
||||
var aName = pa[i][0];
|
||||
var bName = pb[i][0];
|
||||
assertEq(aName, bName, path);
|
||||
|
||||
var path2 = isIndex(aName) ? path + "[" + aName + "]" : path + "." + aName;
|
||||
var da = pa[i][1];
|
||||
var db = pb[i][1];
|
||||
assertEq(da.configurable, shouldBeConfigurable(ca, aName), path2);
|
||||
assertEq(da.writable, shouldBeWritable(ca, aName), path2);
|
||||
assertEq("value" in da, true, path2);
|
||||
var va = da.value;
|
||||
var vb = b[pb[i][0]];
|
||||
stack.push([va, vb, path2]);
|
||||
}
|
||||
}
|
||||
|
||||
function isClone(a, b) {
|
||||
var stack = [[a, b]];
|
||||
var stack = [[a, b, 'obj']];
|
||||
var memory = new WeakMap();
|
||||
var rmemory = new WeakMap();
|
||||
|
||||
while (stack.length > 0) {
|
||||
var pair = stack.pop();
|
||||
var x = pair[0], y = pair[1];
|
||||
var x = pair[0], y = pair[1], path = pair[2];
|
||||
if (typeof x !== "object" || x === null) {
|
||||
// x is primitive.
|
||||
if (x !== y)
|
||||
return false;
|
||||
assertEq(x, y, "equal primitives");
|
||||
} else if (x instanceof Date) {
|
||||
if (x.getTime() !== y.getTime())
|
||||
return false;
|
||||
assertEq(x.getTime(), y.getTime(), "equal times for cloned Dates");
|
||||
} else if (memory.has(x)) {
|
||||
// x is an object we have seen before in a.
|
||||
if (y !== memory.get(x))
|
||||
return false;
|
||||
assertEq(rmemory.get(y), x);
|
||||
assertEq(y, memory.get(x), "repeated object the same");
|
||||
assertEq(rmemory.get(y), x, "repeated object's clone already seen");
|
||||
} else {
|
||||
// x is an object we have not seen before.
|
||||
// Check that we have not seen y before either.
|
||||
if (rmemory.has(y))
|
||||
return false;
|
||||
// Check that we have not seen y before either.
|
||||
assertEq(rmemory.has(y), false);
|
||||
|
||||
// x and y must be of the same [[Class]].
|
||||
var xcls = Object.prototype.toString.call(x);
|
||||
var ycls = Object.prototype.toString.call(y);
|
||||
if (xcls !== ycls)
|
||||
return false;
|
||||
var xcls = classOf(x);
|
||||
var ycls = classOf(y);
|
||||
assertEq(xcls, ycls, "same [[Class]]");
|
||||
|
||||
// This function is only designed to check Objects and Arrays.
|
||||
assertEq(xcls === "[object Object]" || xcls === "[object Array]",
|
||||
true);
|
||||
// clone objects should have the default prototype of the class
|
||||
assertEq(Object.getPrototypeOf(x), this[xcls].prototype);
|
||||
|
||||
// Compare objects.
|
||||
var xk = Object.keys(x), yk = Object.keys(y);
|
||||
if (xk.length !== yk.length)
|
||||
return false;
|
||||
for (var i = 0; i < xk.length; i++) {
|
||||
// We must see the same property names in the same order.
|
||||
if (xk[i] !== yk[i])
|
||||
return false;
|
||||
|
||||
// Put the property values on the stack to compare later.
|
||||
stack.push([x[xk[i]], y[yk[i]]]);
|
||||
}
|
||||
compareProperties(x, y, stack, path);
|
||||
|
||||
// Record that we have seen this pair of objects.
|
||||
memory.set(x, y);
|
||||
@ -60,13 +150,14 @@ function isClone(a, b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function check(a) {
|
||||
assertEq(isClone(a, deserialize(serialize(a))), true);
|
||||
function check(val) {
|
||||
var clone = deserialize(serialize(val));
|
||||
assertEq(isClone(val, clone), true);
|
||||
return clone;
|
||||
}
|
||||
|
||||
// Various recursive objects, i.e. those which the structured cloning
|
||||
// algorithm wants us to reject due to "memory".
|
||||
//
|
||||
// Various recursive objects
|
||||
|
||||
// Recursive array.
|
||||
var a = [];
|
||||
a[0] = a;
|
||||
@ -103,4 +194,120 @@ b[0] = {owner: a};
|
||||
b[1] = [];
|
||||
check(a);
|
||||
|
||||
// Date objects should not be identical even if representing the same date
|
||||
var ar = [ new Date(1000), new Date(1000) ];
|
||||
var clone = check(ar);
|
||||
assertEq(clone[0] === clone[1], false);
|
||||
|
||||
// Identity preservation for various types of objects
|
||||
|
||||
function checkSimpleIdentity(v)
|
||||
{
|
||||
a = check([ v, v ]);
|
||||
assertEq(a[0] === a[1], true);
|
||||
return a;
|
||||
}
|
||||
|
||||
var v = new Boolean(true);
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = new Number(17);
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = new String("yo");
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = "fish";
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = new Int8Array([ 10, 20 ]);
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = new ArrayBuffer(7);
|
||||
checkSimpleIdentity(v);
|
||||
|
||||
v = new Date(1000);
|
||||
b = [ v, v, { 'date': v } ];
|
||||
clone = check(b);
|
||||
assertEq(clone[0] === clone[1], true);
|
||||
assertEq(clone[0], clone[2]['date']);
|
||||
assertEq(clone[0] === v, false);
|
||||
|
||||
// Reduced and modified from postMessage_structured_clone test
|
||||
let foo = { };
|
||||
let baz = { };
|
||||
let obj = { 'foo': foo,
|
||||
'bar': { 'foo': foo },
|
||||
'expando': { 'expando': baz },
|
||||
'baz': baz };
|
||||
check(obj);
|
||||
|
||||
for (var obj of new getTestContent)
|
||||
check(obj);
|
||||
|
||||
// Stolen wholesale from postMessage_structured_clone_helper.js
|
||||
function getTestContent()
|
||||
{
|
||||
yield "hello";
|
||||
yield 2+3;
|
||||
yield 12;
|
||||
yield null;
|
||||
yield "complex" + "string";
|
||||
yield new Object();
|
||||
yield new Date(1306113544);
|
||||
yield [1, 2, 3, 4, 5];
|
||||
let obj = new Object();
|
||||
obj.foo = 3;
|
||||
obj.bar = "hi";
|
||||
obj.baz = new Date(1306113544);
|
||||
obj.boo = obj;
|
||||
yield obj;
|
||||
|
||||
let recursiveobj = new Object();
|
||||
recursiveobj.a = recursiveobj;
|
||||
recursiveobj.foo = new Object();
|
||||
recursiveobj.foo.bar = "bar";
|
||||
recursiveobj.foo.backref = recursiveobj;
|
||||
recursiveobj.foo.baz = 84;
|
||||
recursiveobj.foo.backref2 = recursiveobj;
|
||||
recursiveobj.bar = new Object();
|
||||
recursiveobj.bar.foo = "foo";
|
||||
recursiveobj.bar.backref = recursiveobj;
|
||||
recursiveobj.bar.baz = new Date(1306113544);
|
||||
recursiveobj.bar.backref2 = recursiveobj;
|
||||
recursiveobj.expando = recursiveobj;
|
||||
yield recursiveobj;
|
||||
|
||||
let obj = new Object();
|
||||
obj.expando1 = 1;
|
||||
obj.foo = new Object();
|
||||
obj.foo.bar = 2;
|
||||
obj.bar = new Object();
|
||||
obj.bar.foo = obj.foo;
|
||||
obj.expando = new Object();
|
||||
obj.expando.expando = new Object();
|
||||
obj.expando.expando.obj = obj;
|
||||
obj.expando2 = 4;
|
||||
obj.baz = obj.expando.expando;
|
||||
obj.blah = obj.bar;
|
||||
obj.foo.baz = obj.blah;
|
||||
obj.foo.blah = obj.blah;
|
||||
yield obj;
|
||||
|
||||
let diamond = new Object();
|
||||
let obj = new Object();
|
||||
obj.foo = "foo";
|
||||
obj.bar = 92;
|
||||
obj.backref = diamond;
|
||||
diamond.ref1 = obj;
|
||||
diamond.ref2 = obj;
|
||||
yield diamond;
|
||||
|
||||
let doubleref = new Object();
|
||||
let obj = new Object();
|
||||
doubleref.ref1 = obj;
|
||||
doubleref.ref2 = obj;
|
||||
yield doubleref;
|
||||
}
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
||||
|
@ -1,6 +1,10 @@
|
||||
import re
|
||||
from subprocess import list2cmdline
|
||||
from progressbar import NullProgressBar, ProgressBar
|
||||
import pipes
|
||||
|
||||
# subprocess.list2cmdline does not properly escape for sh-like shells
|
||||
def escape_cmdline(args):
|
||||
return ' '.join([ pipes.quote(a) for a in args ])
|
||||
|
||||
class TestOutput:
|
||||
"""Output from a test run."""
|
||||
@ -107,7 +111,7 @@ class ResultsSink:
|
||||
self.n += 1
|
||||
else:
|
||||
if self.options.show_cmd:
|
||||
print >> self.fp, list2cmdline(output.cmd)
|
||||
print >> self.fp, escape_cmdline(output.cmd)
|
||||
|
||||
if self.options.show_output:
|
||||
print >> self.fp, ' rc = %d, run time = %f' % (output.rc, output.dt)
|
||||
|
@ -5066,11 +5066,40 @@ nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewportInfo vInf =
|
||||
nsContentUtils::GetViewportInfo(aPresContext->PresShell()->GetDocument());
|
||||
// XXXjwir3:
|
||||
// See bug 706918, comment 23 for more information on this particular section
|
||||
// of the code. We're using "screen size" in place of the size of the content
|
||||
// area, because on mobile, these are close or equal. This will work for our
|
||||
// purposes (bug 706198), but it will need to be changed in the future to be
|
||||
// more correct when we bring the rest of the viewport code into platform.
|
||||
// We actually want the size of the content area, in the event that we don't
|
||||
// have any metadata about the width and/or height. On mobile, the screen size
|
||||
// and the size of the content area are very close, or the same value.
|
||||
// In XUL fennec, the content area is the size of the <browser> widget, but
|
||||
// in native fennec, the content area is the size of the Gecko LayerView
|
||||
// object.
|
||||
|
||||
if (vInf.defaultZoom >= 1.0 || vInf.autoSize) {
|
||||
return false;
|
||||
// TODO:
|
||||
// Once bug 716575 has been resolved, this code should be changed so that it
|
||||
// does the right thing on all platforms.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIScreenManager> screenMgr =
|
||||
do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
|
||||
if (screen) {
|
||||
int32_t screenLeft, screenTop, screenWidth, screenHeight;
|
||||
screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
|
||||
|
||||
ViewportInfo vInf =
|
||||
nsContentUtils::GetViewportInfo(aPresContext->PresShell()->GetDocument(),
|
||||
screenWidth, screenHeight);
|
||||
|
||||
if (vInf.defaultZoom >= 1.0 || vInf.autoSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
using namespace MPAPI;
|
||||
|
||||
const int OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka = 0x7FA30C01;
|
||||
|
||||
namespace android {
|
||||
|
||||
// MediaStreamSource is a DataSource that reads from a MPAPI media stream.
|
||||
|
@ -9,6 +9,8 @@ include protocol PNecko;
|
||||
include InputStreamParams;
|
||||
include URIParams;
|
||||
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
include "SerializedLoadContext.h";
|
||||
|
||||
using IPC::SerializedLoadContext;
|
||||
|
@ -9,6 +9,8 @@ include protocol PNecko;
|
||||
include InputStreamParams;
|
||||
include URIParams;
|
||||
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
include "mozilla/net/PHttpChannelParams.h";
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
include "prio.h";
|
||||
|
@ -10,6 +10,8 @@ include protocol PBrowser;
|
||||
include InputStreamParams;
|
||||
include URIParams;
|
||||
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
include "SerializedLoadContext.h";
|
||||
using IPC::SerializedLoadContext;
|
||||
|
||||
|
@ -190,6 +190,58 @@ AbstractFile.normalizeToPointer = function normalizeToPointer(candidate, bytes,
|
||||
return {ptr: ptr, bytes: bytes};
|
||||
};
|
||||
|
||||
/**
|
||||
* Code shared by iterators.
|
||||
*/
|
||||
AbstractFile.AbstractIterator = function AbstractIterator() {
|
||||
};
|
||||
AbstractFile.AbstractIterator.prototype = {
|
||||
/**
|
||||
* Allow iterating with |for|
|
||||
*/
|
||||
__iterator__: function __iterator__() {
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Apply a function to all elements of the directory sequentially.
|
||||
*
|
||||
* @param {Function} cb This function will be applied to all entries
|
||||
* of the directory. It receives as arguments
|
||||
* - the OS.File.Entry corresponding to the entry;
|
||||
* - the index of the entry in the enumeration;
|
||||
* - the iterator itself - calling |close| on the iterator stops
|
||||
* the loop.
|
||||
*/
|
||||
forEach: function forEach(cb) {
|
||||
let index = 0;
|
||||
for (let entry in this) {
|
||||
cb(entry, index++, this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Return several entries at once.
|
||||
*
|
||||
* Entries are returned in the same order as a walk with |forEach| or
|
||||
* |for(...)|.
|
||||
*
|
||||
* @param {number=} length If specified, the number of entries
|
||||
* to return. If unspecified, return all remaining entries.
|
||||
* @return {Array} An array containing the next |length| entries, or
|
||||
* less if the iteration contains less than |length| entries left.
|
||||
*/
|
||||
nextBatch: function nextBatch(length) {
|
||||
let array = [];
|
||||
let i = 0;
|
||||
for (let entry in this) {
|
||||
array.push(entry);
|
||||
if (++i >= length) {
|
||||
return array;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function shared by implementations of |OS.File.open|:
|
||||
* extract read/write/trunc/create/existing flags from a |mode|
|
||||
|
@ -583,53 +583,51 @@
|
||||
* @constructor
|
||||
*/
|
||||
File.DirectoryIterator = function DirectoryIterator(path, options) {
|
||||
exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
|
||||
let dir = throw_on_null("DirectoryIterator", UnixFile.opendir(path));
|
||||
this._dir = dir;
|
||||
this._path = path;
|
||||
};
|
||||
File.DirectoryIterator.prototype = {
|
||||
__iterator__: function __iterator__() {
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* Return the next entry in the directory, if any such entry is
|
||||
* available.
|
||||
*
|
||||
* Skip special directories "." and "..".
|
||||
*
|
||||
* @return {File.Entry} The next entry in the directory.
|
||||
* @throws {StopIteration} Once all files in the directory have been
|
||||
* encountered.
|
||||
*/
|
||||
next: function next() {
|
||||
if (!this._dir) {
|
||||
throw StopIteration;
|
||||
}
|
||||
for (let entry = UnixFile.readdir(this._dir);
|
||||
entry != null && !entry.isNull();
|
||||
entry = UnixFile.readdir(this._dir)) {
|
||||
let contents = entry.contents;
|
||||
if (contents.d_type == OS.Constants.libc.DT_DIR) {
|
||||
let name = contents.d_name.readString();
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return new File.DirectoryIterator.Entry(contents, this._path);
|
||||
}
|
||||
this.close();
|
||||
throw StopIteration;
|
||||
},
|
||||
File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
|
||||
|
||||
/**
|
||||
* Close the iterator and recover all resources.
|
||||
* You should call this once you have finished iterating on a directory.
|
||||
*/
|
||||
close: function close() {
|
||||
if (!this._dir) return;
|
||||
UnixFile.closedir(this._dir);
|
||||
this._dir = null;
|
||||
/**
|
||||
* Return the next entry in the directory, if any such entry is
|
||||
* available.
|
||||
*
|
||||
* Skip special directories "." and "..".
|
||||
*
|
||||
* @return {File.Entry} The next entry in the directory.
|
||||
* @throws {StopIteration} Once all files in the directory have been
|
||||
* encountered.
|
||||
*/
|
||||
File.DirectoryIterator.prototype.next = function next() {
|
||||
if (!this._dir) {
|
||||
throw StopIteration;
|
||||
}
|
||||
for (let entry = UnixFile.readdir(this._dir);
|
||||
entry != null && !entry.isNull();
|
||||
entry = UnixFile.readdir(this._dir)) {
|
||||
let contents = entry.contents;
|
||||
if (contents.d_type == OS.Constants.libc.DT_DIR) {
|
||||
let name = contents.d_name.readString();
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return new File.DirectoryIterator.Entry(contents, this._path);
|
||||
}
|
||||
this.close();
|
||||
throw StopIteration;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the iterator and recover all resources.
|
||||
* You should call this once you have finished iterating on a directory.
|
||||
*/
|
||||
File.DirectoryIterator.prototype.close = function close() {
|
||||
if (!this._dir) return;
|
||||
UnixFile.closedir(this._dir);
|
||||
this._dir = null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -480,6 +480,7 @@
|
||||
* @constructor
|
||||
*/
|
||||
File.DirectoryIterator = function DirectoryIterator(path, options) {
|
||||
exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
|
||||
if (options && options.winPattern) {
|
||||
this._pattern = path + "\\" + options.winPattern;
|
||||
} else {
|
||||
@ -488,18 +489,22 @@
|
||||
this._handle = null;
|
||||
this._path = path;
|
||||
this._started = false;
|
||||
this._closed = false;
|
||||
};
|
||||
File.DirectoryIterator.prototype = {
|
||||
__iterator__: function __iterator__() {
|
||||
return this;
|
||||
},
|
||||
File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
|
||||
|
||||
/**
|
||||
* Fetch the next entry in the directory.
|
||||
*
|
||||
* @return null If we have reached the end of the directory.
|
||||
*/
|
||||
_next: function _next() {
|
||||
File.DirectoryIterator.prototype._next = function _next() {
|
||||
// Bailout if the iterator is closed. Note that this may
|
||||
// happen even before it is fully initialized.
|
||||
if (this._closed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Iterator is not fully initialized yet. Finish
|
||||
// initialization.
|
||||
if (!this._started) {
|
||||
@ -517,11 +522,6 @@
|
||||
return gFindData;
|
||||
}
|
||||
|
||||
// We have closed this iterator already.
|
||||
if (!this._handle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (WinFile.FindNextFile(this._handle, gFindDataPtr)) {
|
||||
return gFindData;
|
||||
} else {
|
||||
@ -544,7 +544,7 @@
|
||||
* @throws {StopIteration} Once all files in the directory have been
|
||||
* encountered.
|
||||
*/
|
||||
next: function next() {
|
||||
File.DirectoryIterator.prototype.next = function next() {
|
||||
// FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
|
||||
// that "." and ".." are absolutely normal file names if _path starts
|
||||
// with such prefix
|
||||
@ -556,15 +556,22 @@
|
||||
return new File.DirectoryIterator.Entry(entry, this._path);
|
||||
}
|
||||
throw StopIteration;
|
||||
},
|
||||
close: function close() {
|
||||
if (!this._handle) {
|
||||
return;
|
||||
}
|
||||
WinFile.FindClose(this._handle);
|
||||
};
|
||||
|
||||
File.DirectoryIterator.prototype.close = function close() {
|
||||
if (this._closed) {
|
||||
return;
|
||||
}
|
||||
this._closed = true;
|
||||
if (this._handle) {
|
||||
// We might not have a handle if the iterator is closed
|
||||
// before being used.
|
||||
throw_on_zero("FindClose",
|
||||
WinFile.FindClose(this._handle));
|
||||
this._handle = null;
|
||||
}
|
||||
};
|
||||
|
||||
File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
|
||||
// Copy the relevant part of |win_entry| to ensure that
|
||||
// our data is not overwritten prematurely.
|
||||
|
@ -449,6 +449,63 @@ function test_iter_dir()
|
||||
|
||||
ok(true, "test_iter_dir: Cleaning up");
|
||||
iterator.close();
|
||||
|
||||
// Testing nextBatch()
|
||||
iterator = new OS.File.DirectoryIterator(parent);
|
||||
let allentries = [x for(x in iterator)];
|
||||
iterator.close();
|
||||
|
||||
ok(allentries.length >= 14, "test_iter_dir: Meta-check: the test directory should contain at least 14 items");
|
||||
|
||||
iterator = new OS.File.DirectoryIterator(parent);
|
||||
let firstten = iterator.nextBatch(10);
|
||||
is(firstten.length, 10, "test_iter_dir: nextBatch(10) returns 10 items");
|
||||
for (let i = 0; i < firstten.length; ++i) {
|
||||
is(allentries[i].path, firstten[i].path, "test_iter_dir: Checking that batch returns the correct entries");
|
||||
}
|
||||
let nextthree = iterator.nextBatch(3);
|
||||
is(nextthree.length, 3, "test_iter_dir: nextBatch(3) returns 3 items");
|
||||
for (let i = 0; i < nextthree.length; ++i) {
|
||||
is(allentries[i + firstten.length].path, nextthree[i].path, "test_iter_dir: Checking that batch 2 returns the correct entries");
|
||||
}
|
||||
let everythingelse = iterator.nextBatch();
|
||||
ok(everythingelse.length >= 1, "test_iter_dir: nextBatch() returns at least one item");
|
||||
for (let i = 0; i < everythingelse.length; ++i) {
|
||||
is(allentries[i + firstten.length + nextthree.length].path, everythingelse[i].path, "test_iter_dir: Checking that batch 3 returns the correct entries");
|
||||
}
|
||||
is(iterator.nextBatch().length, 0, "test_iter_dir: Once there is nothing left, nextBatch returns an empty array");
|
||||
iterator.close();
|
||||
|
||||
iterator = new OS.File.DirectoryIterator(parent);
|
||||
iterator.close();
|
||||
is(iterator.nextBatch().length, 0, "test_iter_dir: nextBatch on closed iterator returns an empty array");
|
||||
|
||||
iterator = new OS.File.DirectoryIterator(parent);
|
||||
let allentries2 = iterator.nextBatch();
|
||||
is(allentries.length, allentries2.length, "test_iter_dir: Checking that getBatch(null) returns the right number of entries");
|
||||
for (let i = 0; i < allentries.length; ++i) {
|
||||
is(allentries[i].path, allentries2[i].path, "test_iter_dir: Checking that getBatch(null) returns everything in the right order");
|
||||
}
|
||||
iterator.close();
|
||||
|
||||
// Test forEach
|
||||
iterator = new OS.File.DirectoryIterator(parent);
|
||||
let index = 0;
|
||||
iterator.forEach(
|
||||
function cb(entry, aIndex, aIterator) {
|
||||
is(index, aIndex, "test_iter_dir: Checking that forEach index is correct");
|
||||
ok(iterator == aIterator, "test_iter_dir: Checking that right iterator is passed");
|
||||
if (index < 10) {
|
||||
is(allentries[index].path, entry.path, "test_iter_dir: Checking that forEach entry is correct");
|
||||
} else if (index == 10) {
|
||||
iterator.close();
|
||||
} else {
|
||||
ok(false, "test_iter_dir: Checking that forEach can be stopped early");
|
||||
}
|
||||
++index;
|
||||
});
|
||||
iterator.close();
|
||||
|
||||
ok(true, "test_iter_dir: Complete");
|
||||
}
|
||||
|
||||
|
@ -1921,6 +1921,12 @@
|
||||
"n_buckets": 10,
|
||||
"description": "Firefox: Time taken by the tab closing animation in milliseconds"
|
||||
},
|
||||
"FX_TAB_SWITCH_UPDATE_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "1000",
|
||||
"n_buckets": 20,
|
||||
"description": "Firefox: Time in ms spent updating UI in response to a tab switch"
|
||||
},
|
||||
"FX_KEYWORD_URL_USERSET": {
|
||||
"kind": "boolean",
|
||||
"description": "Firefox: keyword.URL has a user-set value"
|
||||
|
@ -35,6 +35,7 @@ const PREFS_WHITELIST = [
|
||||
"browser.sessionstore.",
|
||||
"browser.startup.homepage",
|
||||
"browser.tabs.",
|
||||
"browser.urlbar.",
|
||||
"browser.zoom.",
|
||||
"dom.",
|
||||
"extensions.checkCompatibility",
|
||||
|
@ -31,8 +31,13 @@ let tests = [
|
||||
|
||||
function snapshotSchema(done) {
|
||||
Troubleshoot.snapshot(function (snapshot) {
|
||||
validateObject(snapshot, SNAPSHOT_SCHEMA);
|
||||
ok(true, "The snapshot should conform to the schema.");
|
||||
try {
|
||||
validateObject(snapshot, SNAPSHOT_SCHEMA);
|
||||
ok(true, "The snapshot should conform to the schema.");
|
||||
}
|
||||
catch (err) {
|
||||
ok(false, err);
|
||||
}
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
@ -92,12 +92,9 @@
|
||||
onset="if (val) this.setAttribute('clickSelectsAll', 'true');
|
||||
else this.removeAttribute('clickSelectsAll'); return val;" />
|
||||
|
||||
<property name="editor" readonly="true">
|
||||
<getter><![CDATA[
|
||||
const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
|
||||
return this.inputField.QueryInterface(nsIDOMNSEditableElement).editor;
|
||||
]]></getter>
|
||||
</property>
|
||||
<field name="editor" readonly="true">
|
||||
this.inputField.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor;
|
||||
</field>
|
||||
|
||||
<method name="reset">
|
||||
<body><![CDATA[
|
||||
@ -217,7 +214,7 @@
|
||||
|
||||
<handler event="blur" phase="capturing">
|
||||
<![CDATA[
|
||||
this.removeAttribute('focused');
|
||||
this.removeAttribute("focused");
|
||||
|
||||
// don't trigger clickSelectsAll when switching application windows
|
||||
if (window == window.top &&
|
||||
|
@ -11,7 +11,7 @@ interface nsIURI;
|
||||
interface nsIDOMWindow;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
[scriptable, uuid(0d79adad-b244-49A5-9997-2a8cad93fc44)]
|
||||
[scriptable, function, uuid(0d79adad-b244-49A5-9997-2a8cad93fc44)]
|
||||
interface nsIFilePickerShownCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -731,33 +731,56 @@ const nsTArray<GfxDriverInfo>&
|
||||
GfxInfo::GetGfxDriverInfo()
|
||||
{
|
||||
if (!mDriverInfo->Length()) {
|
||||
/*
|
||||
* It should be noted here that more specialized rules on certain features
|
||||
* should be inserted -before- more generalized restriction. As the first
|
||||
* match for feature/OS/device found in the list will be used for the final
|
||||
* blacklisting call.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NVIDIA entries
|
||||
*/
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_XP,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(6,14,12,5721), "257.21" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_VISTA,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_7,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_XP,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(6,14,11,8265), "182.65" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_VISTA,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,17,11,8265), "182.65" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_7,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,17,11,8265), "182.65" );
|
||||
|
||||
/*
|
||||
* AMD/ATI entries
|
||||
*/
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
|
||||
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,62,0,0), "9.6" );
|
||||
APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
|
||||
DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
|
||||
DRIVER_LESS_THAN, V(8,62,0,0), "9.6" );
|
||||
|
||||
/*
|
||||
* Bug 783517 - crashes in AMD driver on Windows 8
|
||||
@ -807,27 +830,46 @@ GfxInfo::GetGfxDriverInfo()
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(devFamily), \
|
||||
GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, \
|
||||
DRIVER_LESS_THAN, driverVer )
|
||||
#define IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(winVer, devFamily, driverVer) \
|
||||
APPEND_TO_DRIVER_BLOCKLIST2( winVer, \
|
||||
(nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(devFamily), \
|
||||
nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, \
|
||||
DRIVER_LESS_THAN, driverVer )
|
||||
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA500, V(6,14,11,1018));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA500, V(7,14,10,1006));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA900, GfxDriverInfo::allDriverVersions);
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA950, V(7,14,10,1504));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150, V(7,14,10,2124));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMAX3000, V(7,15,10,1666));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,2202));
|
||||
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA500, V(5,0,0,2026));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA900, GfxDriverInfo::allDriverVersions);
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA950, V(8,15,10,1930));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA3150, V(8,14,10,2117));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMAX3000, V(8,15,10,1930));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,2202));
|
||||
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA500, V(3,0,20,3200));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA900, V(6,14,10,4764));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA950, V(6,14,10,4926));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA3150, V(6,14,10,5260));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA3150, V(6,14,10,5134));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMAX3000, V(6,14,10,5218));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMAX4500HD, V(6,14,10,5284));
|
||||
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA500, V(7,14,10,1006));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA500, V(3,0,20,3200));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA900, GfxDriverInfo::allDriverVersions);
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA950, V(7,14,10,1504));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150, V(7,14,10,2124));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150, V(7,14,10,1910));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX3000, V(7,15,10,1666));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,2202));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,1855));
|
||||
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA500, V(5,0,0,2026));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA900, GfxDriverInfo::allDriverVersions);
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA950, V(8,15,10,1930));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA3150, V(8,14,10,2117));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA3150, V(8,14,10,1972));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX3000, V(8,15,10,1930));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,2202));
|
||||
IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,1855));
|
||||
|
||||
/* OpenGL on any Intel hardware is discouraged */
|
||||
APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_ALL,
|
||||
|
@ -79,57 +79,6 @@ void passed(const char* msg, ...)
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Code profiling
|
||||
//
|
||||
static const char* gCurrentProfile;
|
||||
|
||||
/**
|
||||
* If the build has been configured properly, start the best code profiler
|
||||
* available on this platform.
|
||||
*
|
||||
* This is NOT thread safe.
|
||||
*
|
||||
* @precondition Profiling is not started
|
||||
* @param profileName A descriptive name for this profiling run. Every
|
||||
* attempt is made to name the profile data according
|
||||
* to this name, but check your platform's profiler
|
||||
* documentation for what this means.
|
||||
* @return true if profiling was available and successfully started.
|
||||
* @see StopProfiling
|
||||
*/
|
||||
inline bool
|
||||
StartProfiling(const char* profileName)
|
||||
{
|
||||
NS_ASSERTION(profileName, "need a name for this profile");
|
||||
NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another");
|
||||
|
||||
JSBool ok = JS_StartProfiling(profileName);
|
||||
gCurrentProfile = profileName;
|
||||
return ok ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the platform's profiler. For what this means, what happens after
|
||||
* stopping, and how the profile data can be accessed, check the
|
||||
* documentation of your platform's profiler.
|
||||
*
|
||||
* This is NOT thread safe.
|
||||
*
|
||||
* @precondition Profiling was started
|
||||
* @return true if profiling was successfully stopped.
|
||||
* @see StartProfiling
|
||||
*/
|
||||
inline bool
|
||||
StopProfiling()
|
||||
{
|
||||
NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one");
|
||||
|
||||
const char* profileName = gCurrentProfile;
|
||||
gCurrentProfile = 0;
|
||||
return JS_StopProfiling(profileName) ? true : false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ScopedLogging
|
||||
|
Loading…
Reference in New Issue
Block a user