mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 12:55:46 +00:00
Merge backout
This commit is contained in:
commit
ad5500adec
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "38d1561fe26c12f371c44a47c498722ce06518c2",
|
||||
"revision": "27357877d17b1547c81a26b66e97a27ed7b954ca",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7e93b6de9633a162b735252e8182974456f741f4"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb97461e75cd44d20967bc410b5653dff031ef5a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -671,6 +671,9 @@ pref("plugin.default.state", 2);
|
||||
pref("plugin.default.state", 1);
|
||||
#endif
|
||||
|
||||
// Plugins bundled in XPIs are enabled by default.
|
||||
pref("plugin.defaultXpi.state", 2);
|
||||
|
||||
// Flash is enabled by default, and Java is click-to-activate by default on
|
||||
// all channels.
|
||||
pref("plugin.state.flash", 2);
|
||||
|
@ -197,7 +197,7 @@ let gGrid = {
|
||||
}
|
||||
|
||||
let availSpace = document.documentElement.clientHeight - this._cellMargin -
|
||||
document.querySelector("#newtab-margin-top").offsetHeight;
|
||||
document.querySelector("#newtab-margin-undo-container").offsetHeight;
|
||||
let visibleRows = Math.floor(availSpace / this._cellHeight);
|
||||
this._node.style.height = this._computeHeight() + "px";
|
||||
this._node.style.maxHeight = this._computeHeight(visibleRows) + "px";
|
||||
|
@ -51,16 +51,21 @@ input[type=button] {
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
#newtab-margin-top {
|
||||
#newtab-margin-undo-container {
|
||||
display: -moz-box;
|
||||
height: 40px;
|
||||
-moz-box-align: center;
|
||||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
#newtab-horizontal-margin {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 5;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
#newtab-margin-top,
|
||||
#newtab-margin-bottom {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.newtab-side-margin {
|
||||
|
@ -27,7 +27,10 @@
|
||||
<div id="newtab-scrollbox">
|
||||
|
||||
<div id="newtab-vertical-margin">
|
||||
<div id="newtab-margin-top">
|
||||
|
||||
<div id="newtab-margin-top"/>
|
||||
|
||||
<div id="newtab-margin-undo-container">
|
||||
<div id="newtab-undo-container" undo-disabled="true">
|
||||
<xul:label id="newtab-undo-label"
|
||||
value="&newtab.undo.removedLabel;" />
|
||||
@ -43,8 +46,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<xul:spacer flex="1"/>
|
||||
|
||||
<div id="newtab-horizontal-margin">
|
||||
<div class="newtab-side-margin"/>
|
||||
|
||||
@ -54,7 +55,8 @@
|
||||
<div class="newtab-side-margin"/>
|
||||
</div>
|
||||
|
||||
<xul:spacer flex="1"/>
|
||||
<div id="newtab-margin-bottom"/>
|
||||
|
||||
</div>
|
||||
<input id="newtab-toggle" type="button"/>
|
||||
</div>
|
||||
|
@ -783,7 +783,7 @@ InplaceEditor.prototype = {
|
||||
_onBlur: function InplaceEditor_onBlur(aEvent, aDoNotClear)
|
||||
{
|
||||
if (aEvent && this.popup && this.popup.isOpen &&
|
||||
this.contentType == CONTENT_TYPES.CSS_MIXED) {
|
||||
this.popup.selectedIndex >= 0) {
|
||||
let label, preLabel;
|
||||
if (this._selectedIndex === undefined) {
|
||||
({label, preLabel}) = this.popup.getItemAtIndex(this.popup.selectedIndex);
|
||||
@ -819,6 +819,11 @@ InplaceEditor.prototype = {
|
||||
};
|
||||
this.popup._panel.addEventListener("popuphidden", onPopupHidden);
|
||||
this.popup.hidePopup();
|
||||
// Content type other than CSS_MIXED is used in rule-view where the values
|
||||
// are live previewed. So we apply the value before returning.
|
||||
if (this.contentType != CONTENT_TYPES.CSS_MIXED) {
|
||||
this._apply();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._apply();
|
||||
|
@ -1998,7 +1998,8 @@ TextPropertyEditor.prototype = {
|
||||
get editing() {
|
||||
return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
|
||||
this.ruleEditor.ruleView.colorPicker.tooltip.isShown() ||
|
||||
this.ruleEditor.ruleView.colorPicker.eyedropperOpen);
|
||||
this.ruleEditor.ruleView.colorPicker.eyedropperOpen) ||
|
||||
this.popup.isOpen;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -343,6 +343,7 @@ Experiments.Experiments = function (policy=new Experiments.Policy()) {
|
||||
this._log = Log.repository.getLoggerWithMessagePrefix(
|
||||
"Browser.Experiments.Experiments",
|
||||
"Experiments #" + gExperimentsCounter++ + "::");
|
||||
this._log.trace("constructor");
|
||||
|
||||
this._policy = policy;
|
||||
|
||||
@ -424,9 +425,12 @@ Experiments.Experiments.prototype = {
|
||||
* The promise is fulfilled when all pending tasks are finished.
|
||||
*/
|
||||
uninit: Task.async(function* () {
|
||||
this._log.trace("uninit: started");
|
||||
yield this._loadTask;
|
||||
this._log.trace("uninit: finished with _loadTask");
|
||||
|
||||
if (!this._shutdown) {
|
||||
this._log.trace("uninit: no previous shutdown");
|
||||
this._unregisterWithAddonManager();
|
||||
|
||||
gPrefs.ignore(PREF_LOGGING, configureLogging);
|
||||
@ -443,6 +447,7 @@ Experiments.Experiments.prototype = {
|
||||
this._shutdown = true;
|
||||
if (this._mainTask) {
|
||||
try {
|
||||
this._log.trace("uninit: waiting on _mainTask");
|
||||
yield this._mainTask;
|
||||
} catch (e if e instanceof AlreadyShutdownError) {
|
||||
// We error out of tasks after shutdown via that exception.
|
||||
|
@ -9,7 +9,8 @@
|
||||
}
|
||||
|
||||
/* SCROLLBOX */
|
||||
#newtab-scrollbox:not([page-disabled]) {
|
||||
#newtab-scrollbox:not([page-disabled]),
|
||||
#newtab-scrollbox:not([page-disabled]) #newtab-margin-bottom {
|
||||
color: rgb(0,0,0);
|
||||
background-color: hsl(0,0%,95%);
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
}
|
||||
|
||||
/* SCROLLBOX */
|
||||
#newtab-scrollbox:not([page-disabled]) {
|
||||
#newtab-scrollbox:not([page-disabled]),
|
||||
#newtab-scrollbox:not([page-disabled]) #newtab-margin-bottom {
|
||||
color: rgb(0,0,0);
|
||||
background-color: hsl(0,0%,95%);
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
}
|
||||
|
||||
/* SCROLLBOX */
|
||||
#newtab-scrollbox:not([page-disabled]) {
|
||||
#newtab-scrollbox:not([page-disabled]),
|
||||
#newtab-scrollbox:not([page-disabled]) #newtab-margin-bottom {
|
||||
color: rgb(0,0,0);
|
||||
background-color: hsl(0,0%,95%);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ interface nsIChannel;
|
||||
interface nsIDocShell;
|
||||
interface nsIDomainPolicy;
|
||||
|
||||
[scriptable, uuid(3b6e408b-e774-4612-89e8-3ef303564392)]
|
||||
[scriptable, uuid(4c087cc3-e0cc-4ec3-88df-8d68f3023b45)]
|
||||
interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||
{
|
||||
/**
|
||||
@ -150,11 +150,6 @@ interface nsIScriptSecurityManager : nsIXPCSecurityManager
|
||||
*/
|
||||
[deprecated] nsIPrincipal getCodebasePrincipal(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Return the principal of the specified object in the specified context.
|
||||
*/
|
||||
[implicit_jscontext] nsIPrincipal getObjectPrincipal(in jsval aObject);
|
||||
|
||||
/**
|
||||
* Returns true if the principal of the currently running script is the
|
||||
* system principal, false otherwise.
|
||||
|
@ -1118,18 +1118,6 @@ nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
|
||||
return nsJSPrincipals::get(principals);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetObjectPrincipal(JS::Handle<JS::Value> aObjectVal,
|
||||
JSContext *aCx,
|
||||
nsIPrincipal **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(aObjectVal.isObject(), NS_ERROR_FAILURE);
|
||||
JS::RootedObject obj(aCx, &aObjectVal.toObject());
|
||||
nsCOMPtr<nsIPrincipal> principal = doGetObjectPrincipal(obj);
|
||||
principal.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsIPrincipal*
|
||||
nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
|
||||
|
@ -143,8 +143,9 @@ static const char *kPrefJavaMIME = "plugin.java.mime";
|
||||
// 0.14 force refresh due to locale comparison fix, bug 611296
|
||||
// 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
|
||||
// 0.16 version bump to avoid importing the plugin flags in newer versions
|
||||
// 0.17 added flag on whether plugin is loaded from an XPI
|
||||
// The current plugin registry version (and the maximum version we know how to read)
|
||||
static const char *kPluginRegistryVersion = "0.16";
|
||||
static const char *kPluginRegistryVersion = "0.17";
|
||||
// The minimum registry version we know how to read
|
||||
static const char *kMinimumRegistryVersion = "0.9";
|
||||
|
||||
@ -1627,6 +1628,62 @@ int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
|
||||
return fileModTime;
|
||||
}
|
||||
|
||||
bool
|
||||
GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
|
||||
const nsCOMPtr<nsISimpleEnumerator>& extensionDirs)
|
||||
{
|
||||
if (!extensionDirs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(extensionDirs->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
nsresult rv = extensionDirs->GetNext(getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> extDir(do_QueryInterface(supports, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> dir;
|
||||
if (NS_FAILED(extDir->Clone(getter_AddRefs(dir)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool contains;
|
||||
if (NS_FAILED(dir->Contains(pluginFile, true, &contains)) || !contains) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator>
|
||||
GetExtensionDirectories()
|
||||
{
|
||||
nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
if (!dirService) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> list;
|
||||
nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
|
||||
NS_GET_IID(nsISimpleEnumerator),
|
||||
getter_AddRefs(list));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct CompareFilesByTime
|
||||
{
|
||||
bool
|
||||
@ -1690,6 +1747,11 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
|
||||
pluginFiles.Sort(CompareFilesByTime());
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> extensionDirs = GetExtensionDirectories();
|
||||
if (!extensionDirs) {
|
||||
PLUGIN_LOG(PLUGIN_LOG_ALWAYS, ("Could not get extension directories."));
|
||||
}
|
||||
|
||||
bool warnOutdated = false;
|
||||
|
||||
for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
|
||||
@ -1700,7 +1762,8 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
int64_t fileModTime = GetPluginLastModifiedTime(localfile);
|
||||
const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
|
||||
const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
|
||||
|
||||
// Look for it in our cache
|
||||
NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
|
||||
@ -1781,7 +1844,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginTag = new nsPluginTag(&info, fileModTime);
|
||||
pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
|
||||
pluginFile.FreePluginInfo(info);
|
||||
if (!pluginTag)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -2272,14 +2335,16 @@ nsPluginHost::WritePluginInfo()
|
||||
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
||||
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
||||
|
||||
// lastModifiedTimeStamp|canUnload|tag->mFlags
|
||||
PR_fprintf(fd, "%lld%c%d%c%lu%c%c\n",
|
||||
// lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
|
||||
PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
|
||||
tag->mLastModifiedTime,
|
||||
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
||||
false, // did store whether or not to unload in-process plugins
|
||||
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
||||
0, // legacy field for flags
|
||||
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
||||
tag->IsFromExtension(),
|
||||
PLUGIN_REGISTRY_FIELD_DELIMITER,
|
||||
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
|
||||
|
||||
//description, name & mtypecount are on separate line
|
||||
@ -2486,20 +2551,23 @@ nsPluginHost::ReadPluginInfo()
|
||||
}
|
||||
|
||||
// Registry v0.13 and upwards includes the list of invalid plugins
|
||||
bool hasInvalidPlugins = (version >= "0.13");
|
||||
const bool hasInvalidPlugins = (version >= "0.13");
|
||||
|
||||
// Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
|
||||
const bool hasValidFlags = (version < "0.16");
|
||||
|
||||
if (!ReadSectionHeader(reader, "PLUGINS"))
|
||||
return rv;
|
||||
// Registry v0.17 and upwards store whether the plugin comes from an XPI.
|
||||
const bool hasFromExtension = (version >= "0.17");
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
bool hasFullPathInFileNameField = false;
|
||||
const bool hasFullPathInFileNameField = false;
|
||||
#else
|
||||
bool hasFullPathInFileNameField = (version < "0.11");
|
||||
const bool hasFullPathInFileNameField = (version < "0.11");
|
||||
#endif
|
||||
|
||||
if (!ReadSectionHeader(reader, "PLUGINS"))
|
||||
return rv;
|
||||
|
||||
while (reader.NextLine()) {
|
||||
const char *filename;
|
||||
const char *fullpath;
|
||||
@ -2545,13 +2613,18 @@ nsPluginHost::ReadPluginInfo()
|
||||
version = "0";
|
||||
}
|
||||
|
||||
// lastModifiedTimeStamp|canUnload|tag.mFlag
|
||||
if (reader.ParseLine(values, 3) != 3)
|
||||
// lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
|
||||
const int count = hasFromExtension ? 4 : 3;
|
||||
if (reader.ParseLine(values, count) != count)
|
||||
return rv;
|
||||
|
||||
// If this is an old plugin registry mark this plugin tag to be refreshed
|
||||
int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
|
||||
uint32_t tagflag = atoi(values[2]);
|
||||
bool fromExtension = false;
|
||||
if (hasFromExtension) {
|
||||
fromExtension = atoi(values[3]);
|
||||
}
|
||||
if (!reader.NextLine())
|
||||
return rv;
|
||||
|
||||
@ -2622,7 +2695,7 @@ nsPluginHost::ReadPluginInfo()
|
||||
(const char* const*)mimetypes,
|
||||
(const char* const*)mimedescriptions,
|
||||
(const char* const*)extensions,
|
||||
mimetypecount, lastmod, true);
|
||||
mimetypecount, lastmod, fromExtension, true);
|
||||
if (heapalloced)
|
||||
delete [] heapalloced;
|
||||
|
||||
|
@ -30,6 +30,9 @@ using namespace mozilla;
|
||||
// no longer used 0x0008 // reuse only if regenerating pluginreg.dat
|
||||
#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
|
||||
|
||||
static const char kPrefDefaultEnabledState[] = "plugin.default.state";
|
||||
static const char kPrefDefaultEnabledStateXpi[] = "plugin.defaultXpi.state";
|
||||
|
||||
inline char* new_str(const char* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
@ -62,7 +65,9 @@ GetStatePrefNameForPlugin(nsPluginTag* aTag)
|
||||
|
||||
/* nsPluginTag */
|
||||
|
||||
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime)
|
||||
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension)
|
||||
: mName(aPluginInfo->fName),
|
||||
mDescription(aPluginInfo->fDescription),
|
||||
mLibrary(nullptr),
|
||||
@ -74,7 +79,8 @@ nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime)
|
||||
mLastModifiedTime(aLastModifiedTime),
|
||||
mNiceFileName(),
|
||||
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
|
||||
mCachedBlocklistStateValid(false)
|
||||
mCachedBlocklistStateValid(false),
|
||||
mIsFromExtension(fromExtension)
|
||||
{
|
||||
InitMime(aPluginInfo->fMimeTypeArray,
|
||||
aPluginInfo->fMimeDescriptionArray,
|
||||
@ -94,6 +100,7 @@ nsPluginTag::nsPluginTag(const char* aName,
|
||||
const char* const* aExtensions,
|
||||
int32_t aVariants,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension,
|
||||
bool aArgsAreUTF8)
|
||||
: mName(aName),
|
||||
mDescription(aDescription),
|
||||
@ -106,7 +113,8 @@ nsPluginTag::nsPluginTag(const char* aName,
|
||||
mLastModifiedTime(aLastModifiedTime),
|
||||
mNiceFileName(),
|
||||
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
|
||||
mCachedBlocklistStateValid(false)
|
||||
mCachedBlocklistStateValid(false),
|
||||
mIsFromExtension(fromExtension)
|
||||
{
|
||||
InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
|
||||
static_cast<uint32_t>(aVariants));
|
||||
@ -365,8 +373,10 @@ nsPluginTag::GetEnabledState(uint32_t *aEnabledState) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
enabledState = Preferences::GetInt("plugin.default.state",
|
||||
nsIPluginTag::STATE_ENABLED);
|
||||
const char* const pref = mIsFromExtension ? kPrefDefaultEnabledStateXpi
|
||||
: kPrefDefaultEnabledState;
|
||||
|
||||
enabledState = Preferences::GetInt(pref, nsIPluginTag::STATE_ENABLED);
|
||||
if (enabledState >= nsIPluginTag::STATE_DISABLED &&
|
||||
enabledState <= nsIPluginTag::STATE_ENABLED) {
|
||||
*aEnabledState = (uint32_t)enabledState;
|
||||
@ -574,3 +584,8 @@ nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
|
||||
*aLastModifiedTime = mLastModifiedTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool nsPluginTag::IsFromExtension() const
|
||||
{
|
||||
return mIsFromExtension;
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ public:
|
||||
ePluginState_MaxValue = 3,
|
||||
};
|
||||
|
||||
nsPluginTag(nsPluginInfo* aPluginInfo, int64_t aLastModifiedTime);
|
||||
nsPluginTag(nsPluginInfo* aPluginInfo,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension);
|
||||
nsPluginTag(const char* aName,
|
||||
const char* aDescription,
|
||||
const char* aFileName,
|
||||
@ -47,6 +49,7 @@ public:
|
||||
const char* const* aExtensions,
|
||||
int32_t aVariants,
|
||||
int64_t aLastModifiedTime,
|
||||
bool fromExtension,
|
||||
bool aArgsAreUTF8 = false);
|
||||
virtual ~nsPluginTag();
|
||||
|
||||
@ -69,6 +72,8 @@ public:
|
||||
bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
|
||||
nsCString GetNiceFileName();
|
||||
|
||||
bool IsFromExtension() const;
|
||||
|
||||
nsRefPtr<nsPluginTag> mNext;
|
||||
nsCString mName; // UTF-8
|
||||
nsCString mDescription; // UTF-8
|
||||
@ -92,6 +97,7 @@ private:
|
||||
nsCString mNiceFileName; // UTF-8
|
||||
uint16_t mCachedBlocklistState;
|
||||
bool mCachedBlocklistStateValid;
|
||||
bool mIsFromExtension;
|
||||
|
||||
void InitMime(const char* const* aMimeTypes,
|
||||
const char* const* aMimeDescriptions,
|
||||
|
@ -309,6 +309,7 @@ PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
|
||||
}
|
||||
case WM_DESTROY: {
|
||||
EnableWindow(mParentWindow, TRUE);
|
||||
SetForegroundWindow(mParentWindow);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -19,6 +19,9 @@ skip-if = !crashreporter
|
||||
skip-if = !crashreporter
|
||||
[test_hang_submit.xul]
|
||||
skip-if = !crashreporter
|
||||
[test_hangui.xul]
|
||||
skip-if = (!crashreporter) || (os != "win")
|
||||
support-files = hangui_subpage.html hangui_common.js hangui_iface.js dialog_watcher.js
|
||||
[test_idle_hang.xul]
|
||||
skip-if = (!crashreporter) || (os != "win")
|
||||
[test_npruntime.xul]
|
||||
|
172
dom/plugins/test/mochitest/dialog_watcher.js
Normal file
172
dom/plugins/test/mochitest/dialog_watcher.js
Normal file
@ -0,0 +1,172 @@
|
||||
const EVENT_OBJECT_SHOW = 0x8002;
|
||||
const EVENT_OBJECT_HIDE = 0x8003;
|
||||
const WINEVENT_OUTOFCONTEXT = 0;
|
||||
const WINEVENT_SKIPOWNPROCESS = 2;
|
||||
const QS_ALLINPUT = 0x04FF;
|
||||
const INFINITE = 0xFFFFFFFF;
|
||||
const WAIT_OBJECT_0 = 0;
|
||||
const WAIT_TIMEOUT = 258;
|
||||
const PM_NOREMOVE = 0;
|
||||
|
||||
function DialogWatcher(titleText, onDialogStart, onDialogEnd) {
|
||||
this.titleText = titleText;
|
||||
this.onDialogStart = onDialogStart;
|
||||
this.onDialogEnd = onDialogEnd;
|
||||
}
|
||||
|
||||
DialogWatcher.prototype.init = function() {
|
||||
this.hwnd = undefined;
|
||||
if (!this.user32) {
|
||||
this.user32 = ctypes.open("user32.dll");
|
||||
}
|
||||
if (!this.findWindow) {
|
||||
this.findWindow = user32.declare("FindWindowW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.jschar.ptr,
|
||||
ctypes.jschar.ptr);
|
||||
}
|
||||
if (!this.winEventProcType) {
|
||||
this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi,
|
||||
ctypes.void_t,
|
||||
[ctypes.uintptr_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.long,
|
||||
ctypes.long,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t]).ptr;
|
||||
}
|
||||
if (!this.setWinEventHook) {
|
||||
this.setWinEventHook = user32.declare("SetWinEventHook",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uintptr_t,
|
||||
this.winEventProcType,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t);
|
||||
}
|
||||
if (!this.unhookWinEvent) {
|
||||
this.unhookWinEvent = user32.declare("UnhookWinEvent",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int,
|
||||
ctypes.uintptr_t);
|
||||
}
|
||||
if (!this.pointType) {
|
||||
this.pointType = ctypes.StructType("tagPOINT",
|
||||
[ { "x": ctypes.long },
|
||||
{ "y": ctypes.long } ] );
|
||||
}
|
||||
if (!this.msgType) {
|
||||
this.msgType = ctypes.StructType("tagMSG",
|
||||
[ { "hwnd": ctypes.uintptr_t },
|
||||
{ "message": ctypes.uint32_t },
|
||||
{ "wParam": ctypes.uintptr_t },
|
||||
{ "lParam": ctypes.intptr_t },
|
||||
{ "time": ctypes.uint32_t },
|
||||
{ "pt": this.pointType } ] );
|
||||
}
|
||||
if (!this.peekMessage) {
|
||||
this.peekMessage = user32.declare("PeekMessageW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int,
|
||||
this.msgType.ptr,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t);
|
||||
}
|
||||
if (!this.msgWaitForMultipleObjects) {
|
||||
this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uintptr_t.ptr,
|
||||
ctypes.int,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uint32_t);
|
||||
}
|
||||
if (!this.getWindowTextW) {
|
||||
this.getWindowTextW = user32.declare("GetWindowTextW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.jschar.ptr,
|
||||
ctypes.int);
|
||||
}
|
||||
};
|
||||
|
||||
DialogWatcher.prototype.getWindowText = function(hwnd) {
|
||||
var bufType = ctypes.ArrayType(ctypes.jschar);
|
||||
var buffer = new bufType(256);
|
||||
|
||||
if (this.getWindowTextW(hwnd, buffer, buffer.length)) {
|
||||
return buffer.readString();
|
||||
}
|
||||
};
|
||||
|
||||
DialogWatcher.prototype.processWindowEvents = function(timeout) {
|
||||
var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
|
||||
var nhwnd = Number(hwnd)
|
||||
if (event == EVENT_OBJECT_SHOW) {
|
||||
if (nhwnd == self.hwnd) {
|
||||
// We've already picked up this event via FindWindow
|
||||
return;
|
||||
}
|
||||
var windowText = self.getWindowText(hwnd);
|
||||
if (windowText == self.titleText && self.onDialogStart) {
|
||||
self.hwnd = nhwnd;
|
||||
self.onDialogStart(nhwnd);
|
||||
}
|
||||
} else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) {
|
||||
self.onDialogEnd();
|
||||
self.hwnd = null;
|
||||
}
|
||||
};
|
||||
var self = this;
|
||||
var callback = this.winEventProcType(function(hook, event, hwnd, idObject,
|
||||
idChild, dwEventThread,
|
||||
dwmsEventTime) {
|
||||
onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread,
|
||||
dwmsEventTime);
|
||||
} );
|
||||
var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
|
||||
0, callback, 0, 0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
if (!hook) {
|
||||
return;
|
||||
}
|
||||
// Check if the window is already showing
|
||||
var hwnd = this.findWindow(null, this.titleText);
|
||||
if (hwnd && hwnd > 0) {
|
||||
this.hwnd = Number(hwnd);
|
||||
if (this.onDialogStart) {
|
||||
this.onDialogStart(this.hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
timeout = INFINITE;
|
||||
}
|
||||
|
||||
var waitStatus = WAIT_OBJECT_0;
|
||||
var expectingStart = this.onDialogStart && this.hwnd === undefined;
|
||||
while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) {
|
||||
waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ?
|
||||
INFINITE : timeout, 0);
|
||||
if (waitStatus == WAIT_OBJECT_0) {
|
||||
var msg = new this.msgType;
|
||||
this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE);
|
||||
} else if (waitStatus == WAIT_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.unhookWinEvent(hook);
|
||||
// Returns true if the hook was successful, something was found, and we never timed out
|
||||
return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0;
|
||||
};
|
||||
|
20
dom/plugins/test/mochitest/hangui_common.js
Normal file
20
dom/plugins/test/mochitest/hangui_common.js
Normal file
@ -0,0 +1,20 @@
|
||||
// Plugin Hang UI constants
|
||||
const HANGUIOP_NOTHING = 0;
|
||||
const HANGUIOP_CANCEL = 1;
|
||||
const HANGUIOP_COMMAND = 2;
|
||||
const IDC_CONTINUE = 1001;
|
||||
const IDC_STOP = 1002;
|
||||
const IDC_NOFUTURE = 1003;
|
||||
|
||||
// Windows constants
|
||||
const WM_CLOSE = 0x0010;
|
||||
const WM_COMMAND = 0x0111;
|
||||
const BM_GETCHECK = 0x00F0;
|
||||
const BM_SETCHECK = 0x00F1;
|
||||
const BN_CLICKED = 0;
|
||||
const BST_CHECKED = 1;
|
||||
|
||||
// Test-specific constants
|
||||
const EPSILON_MS = 1000;
|
||||
const STALL_DURATION = 2;
|
||||
|
121
dom/plugins/test/mochitest/hangui_iface.js
Normal file
121
dom/plugins/test/mochitest/hangui_iface.js
Normal file
@ -0,0 +1,121 @@
|
||||
var user32;
|
||||
var sendMessage;
|
||||
var getDlgItem;
|
||||
var messageBox;
|
||||
var watcher;
|
||||
|
||||
importScripts("hangui_common.js");
|
||||
importScripts("dialog_watcher.js");
|
||||
|
||||
function initCTypes() {
|
||||
if (!user32) {
|
||||
user32 = ctypes.open("user32.dll");
|
||||
}
|
||||
if (!getDlgItem) {
|
||||
getDlgItem = user32.declare("GetDlgItem",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.int);
|
||||
}
|
||||
if (!sendMessage) {
|
||||
sendMessage = user32.declare("SendMessageW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.intptr_t,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.uint32_t,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.intptr_t);
|
||||
}
|
||||
if (!messageBox) {
|
||||
// Handy for debugging the test itself
|
||||
messageBox = user32.declare("MessageBoxW",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.int,
|
||||
ctypes.uintptr_t,
|
||||
ctypes.jschar.ptr,
|
||||
ctypes.jschar.ptr,
|
||||
ctypes.uint32_t);
|
||||
}
|
||||
if (!watcher) {
|
||||
watcher = new DialogWatcher("Warning: Unresponsive plugin");
|
||||
}
|
||||
}
|
||||
|
||||
function postSuccess(params) {
|
||||
self.postMessage({"status": true, "params": params});
|
||||
}
|
||||
|
||||
function postFail(params, msg) {
|
||||
self.postMessage({"status": false, "params": params, "msg": msg});
|
||||
}
|
||||
|
||||
function onDialogStart(inparams, hwnd) {
|
||||
var params = Object.create(inparams);
|
||||
params.testName += " (Start)";
|
||||
params.callback = null;
|
||||
if (!params.expectToFind) {
|
||||
postFail(params, "Dialog showed when we weren't expecting it to!");
|
||||
return;
|
||||
}
|
||||
if (params.opCode == HANGUIOP_CANCEL) {
|
||||
sendMessage(hwnd, WM_CLOSE, 0, 0);
|
||||
} else if (params.opCode == HANGUIOP_COMMAND) {
|
||||
if (params.check) {
|
||||
var checkbox = getDlgItem(hwnd, IDC_NOFUTURE);
|
||||
if (!checkbox) {
|
||||
postFail(params, "Couldn't find checkbox");
|
||||
return;
|
||||
}
|
||||
sendMessage(checkbox, BM_SETCHECK, BST_CHECKED, 0);
|
||||
sendMessage(hwnd, WM_COMMAND, (BN_CLICKED << 16) | IDC_NOFUTURE, checkbox);
|
||||
}
|
||||
var button = getDlgItem(hwnd, params.commandId);
|
||||
if (!button) {
|
||||
postFail(params,
|
||||
"GetDlgItem failed to find button with ID " + params.commandId);
|
||||
return;
|
||||
}
|
||||
sendMessage(hwnd, WM_COMMAND, (BN_CLICKED << 16) | params.commandId, button);
|
||||
}
|
||||
postSuccess(params);
|
||||
}
|
||||
|
||||
function onDialogEnd(inparams) {
|
||||
var params = Object.create(inparams);
|
||||
params.testName += " (End)";
|
||||
params.callback = inparams.callback;
|
||||
postSuccess(params);
|
||||
}
|
||||
|
||||
self.onmessage = function(event) {
|
||||
initCTypes();
|
||||
watcher.init();
|
||||
var params = event.data;
|
||||
var timeout = params.timeoutMs;
|
||||
if (params.expectToFind) {
|
||||
watcher.onDialogStart = function(hwnd) { onDialogStart(params, hwnd); };
|
||||
if (params.expectToClose) {
|
||||
watcher.onDialogEnd = function() { onDialogEnd(params); };
|
||||
}
|
||||
} else {
|
||||
watcher.onDialogStart = null;
|
||||
watcher.onDialogEnd = null;
|
||||
}
|
||||
var result = watcher.processWindowEvents(timeout);
|
||||
if (result === null) {
|
||||
postFail(params, "Hook failed");
|
||||
} else if (!result) {
|
||||
if (params.expectToFind) {
|
||||
postFail(params, "The dialog didn't show but we were expecting it to");
|
||||
} else {
|
||||
postSuccess(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.onerror = function(event) {
|
||||
var msg = "Error: " + event.message + " at " + event.filename + ":" + event.lineno;
|
||||
postFail(null, msg);
|
||||
};
|
||||
|
4
dom/plugins/test/mochitest/hangui_subpage.html
Normal file
4
dom/plugins/test/mochitest/hangui_subpage.html
Normal file
@ -0,0 +1,4 @@
|
||||
<html>
|
||||
<body onload="window.parent.frameLoaded()">
|
||||
<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
|
||||
|
263
dom/plugins/test/mochitest/test_hangui.xul
Normal file
263
dom/plugins/test/mochitest/test_hangui.xul
Normal file
@ -0,0 +1,263 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Basic Plugin Tests"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<title>Plugin Hang UI Test</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript"
|
||||
src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
|
||||
<script type="application/javascript"
|
||||
src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
|
||||
</body>
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
|
||||
const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
|
||||
const timeoutPref = "dom.ipc.plugins.timeoutSecs";
|
||||
|
||||
var worker = new ChromeWorker("hangui_iface.js");
|
||||
worker.onmessage = function(event) {
|
||||
var result = event.data;
|
||||
var params = result.params;
|
||||
var output = params.testName;
|
||||
if (result.msg) {
|
||||
output += ": " + result.msg;
|
||||
}
|
||||
ok(result.status, output);
|
||||
if (params.callback) {
|
||||
var cb = eval(params.callback);
|
||||
var timeout = setTimeout(function() { clearTimeout(timeout); cb(); }, 100);
|
||||
}
|
||||
};
|
||||
worker.onerror = function(event) {
|
||||
var output = "Error: " + event.message + " at " + event.filename + ":" + event.lineno;
|
||||
ok(false, output);
|
||||
};
|
||||
|
||||
var iframe;
|
||||
var p;
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
|
||||
|
||||
function hanguiOperation(testName, timeoutSec, expectFind, expectClose, opCode,
|
||||
commandId, check, cb) {
|
||||
var timeoutMs = timeoutSec * 1000 + EPSILON_MS;
|
||||
worker.postMessage({ "timeoutMs": timeoutMs, "expectToFind": expectFind,
|
||||
"expectToClose": expectClose, "opCode": opCode,
|
||||
"commandId": commandId, "check": check,
|
||||
"testName": testName, "callback": cb });
|
||||
}
|
||||
|
||||
function hanguiExpect(testName, shouldBeShowing, shouldClose, cb) {
|
||||
var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
|
||||
if (!shouldBeShowing && !timeoutSec) {
|
||||
timeoutSec = Services.prefs.getIntPref(timeoutPref);
|
||||
}
|
||||
hanguiOperation(testName, timeoutSec, shouldBeShowing, shouldClose, HANGUIOP_NOTHING, 0, false, cb);
|
||||
}
|
||||
|
||||
function hanguiContinue(testName, check, cb) {
|
||||
var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
|
||||
hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_CONTINUE, check, cb);
|
||||
}
|
||||
|
||||
function hanguiStop(testName, check, cb) {
|
||||
var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
|
||||
hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_STOP, check, cb);
|
||||
}
|
||||
|
||||
function hanguiCancel(testName, cb) {
|
||||
var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
|
||||
hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_CANCEL, 0, false, cb);
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
if (obsCount > 0) {
|
||||
os.removeObserver(testObserver, "plugin-crashed");
|
||||
--obsCount;
|
||||
}
|
||||
SpecialPowers.clearUserPref(hangUITimeoutPref);
|
||||
SpecialPowers.clearUserPref(hangUIMinDisplayPref);
|
||||
SpecialPowers.clearUserPref(timeoutPref);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
if (!SimpleTest.testPluginIsOOP()) {
|
||||
ok(true, "Skipping this test when test plugin is not OOP.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
resetVars();
|
||||
|
||||
hanguiExpect("Prime ChromeWorker", false, false, "test1");
|
||||
}
|
||||
|
||||
window.frameLoaded = runTests;
|
||||
|
||||
var obsCount = 0;
|
||||
|
||||
function onPluginCrashedHangUI(aEvent) {
|
||||
ok(true, "Plugin crashed notification received");
|
||||
is(aEvent.type, "PluginCrashed", "event is correct type");
|
||||
|
||||
is(p, aEvent.target, "Plugin crashed event target is plugin element");
|
||||
|
||||
ok(aEvent instanceof Components.interfaces.nsIDOMCustomEvent,
|
||||
"plugin crashed event has the right interface");
|
||||
|
||||
var propBag = aEvent.detail.QueryInterface(Components.interfaces.nsIPropertyBag2);
|
||||
var pluginDumpID = propBag.getPropertyAsAString("pluginDumpID");
|
||||
isnot(pluginDumpID, "", "got a non-empty dump ID");
|
||||
var pluginName = propBag.getPropertyAsAString("pluginName");
|
||||
is(pluginName, "Test Plug-in", "got correct plugin name");
|
||||
var pluginFilename = propBag.getPropertyAsAString("pluginFilename");
|
||||
isnot(pluginFilename, "", "got a non-empty filename");
|
||||
var didReport = propBag.getPropertyAsBool("submittedCrashReport");
|
||||
// The app itself may or may not have decided to submit the report, so
|
||||
// allow either true or false here.
|
||||
ok((didReport == true || didReport == false), "event said crash report was submitted");
|
||||
os.removeObserver(testObserver, "plugin-crashed");
|
||||
--obsCount;
|
||||
}
|
||||
|
||||
function resetVars() {
|
||||
iframe = document.getElementById('iframe1');
|
||||
p = iframe.contentDocument.getElementById("plugin1");
|
||||
if (obsCount == 0) {
|
||||
os.addObserver(testObserver, "plugin-crashed", true);
|
||||
++obsCount;
|
||||
}
|
||||
iframe.contentDocument.addEventListener("PluginCrashed",
|
||||
onPluginCrashedHangUI,
|
||||
false);
|
||||
}
|
||||
|
||||
function test9b() {
|
||||
hanguiExpect("test9b: Plugin Hang UI is not showing (checkbox)", false);
|
||||
p.stall(STALL_DURATION);
|
||||
hanguiExpect("test9b: Plugin Hang UI is still not showing (checkbox)", false, false, "finishTest");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
function test9a() {
|
||||
resetVars();
|
||||
SpecialPowers.setIntPref(hangUITimeoutPref, 1);
|
||||
SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
|
||||
SpecialPowers.setIntPref(timeoutPref, 45);
|
||||
hanguiContinue("test9a: Continue button works with checkbox", true, "test9b");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
function test9() {
|
||||
window.frameLoaded = test9a;
|
||||
iframe.contentWindow.location.reload();
|
||||
}
|
||||
|
||||
function test8a() {
|
||||
resetVars();
|
||||
SpecialPowers.setIntPref(hangUITimeoutPref, 1);
|
||||
SpecialPowers.setIntPref(hangUIMinDisplayPref, 4);
|
||||
hanguiExpect("test8a: Plugin Hang UI is not showing (disabled due to hangUIMinDisplaySecs)", false, false, "test9");
|
||||
var exceptionThrown = false;
|
||||
try {
|
||||
p.hang();
|
||||
} catch(e) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
ok(exceptionThrown, "test8a: Exception thrown from hang() when plugin was terminated");
|
||||
}
|
||||
|
||||
function test8() {
|
||||
window.frameLoaded = test8a;
|
||||
iframe.contentWindow.location.reload();
|
||||
}
|
||||
|
||||
function test7a() {
|
||||
resetVars();
|
||||
SpecialPowers.setIntPref(hangUITimeoutPref, 0);
|
||||
hanguiExpect("test7a: Plugin Hang UI is not showing (disabled)", false, false, "test8");
|
||||
var exceptionThrown = false;
|
||||
try {
|
||||
p.hang();
|
||||
} catch(e) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
ok(exceptionThrown, "test7a: Exception thrown from hang() when plugin was terminated");
|
||||
}
|
||||
|
||||
function test7() {
|
||||
window.frameLoaded = test7a;
|
||||
iframe.contentWindow.location.reload();
|
||||
}
|
||||
|
||||
function test6() {
|
||||
SpecialPowers.setIntPref(hangUITimeoutPref, 1);
|
||||
SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
|
||||
SpecialPowers.setIntPref(timeoutPref, 3);
|
||||
hanguiExpect("test6: Plugin Hang UI is showing", true, true, "test7");
|
||||
var exceptionThrown = false;
|
||||
try {
|
||||
p.hang();
|
||||
} catch(e) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
ok(exceptionThrown, "test6: Exception thrown from hang() when plugin was terminated (child timeout)");
|
||||
}
|
||||
|
||||
function test5a() {
|
||||
resetVars();
|
||||
hanguiCancel("test5a: Close button works", "test6");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
function test5() {
|
||||
window.frameLoaded = test5a;
|
||||
iframe.contentWindow.location.reload();
|
||||
}
|
||||
|
||||
function test4() {
|
||||
hanguiStop("test4: Stop button works", false, "test5");
|
||||
// We'll get an exception here because the plugin was terminated
|
||||
var exceptionThrown = false;
|
||||
try {
|
||||
p.hang();
|
||||
} catch(e) {
|
||||
exceptionThrown = true;
|
||||
}
|
||||
ok(exceptionThrown, "test4: Exception thrown from hang() when plugin was terminated");
|
||||
}
|
||||
|
||||
function test3() {
|
||||
hanguiContinue("test3: Continue button works", false, "test4");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
function test2() {
|
||||
// This test is identical to test1 because there were some bugs where the
|
||||
// Hang UI would show on the first hang but not on subsequent hangs
|
||||
hanguiExpect("test2: Plugin Hang UI is showing", true, true, "test3");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
function test1() {
|
||||
SpecialPowers.setIntPref(hangUITimeoutPref, 1);
|
||||
SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
|
||||
SpecialPowers.setIntPref(timeoutPref, 45);
|
||||
hanguiExpect("test1: Plugin Hang UI is showing", true, true, "test2");
|
||||
p.stall(STALL_DURATION);
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
@ -4,7 +4,7 @@
|
||||
# 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/.
|
||||
|
||||
DIRS += ['testplugin']
|
||||
DIRS += ['testplugin', 'testaddon']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
||||
|
||||
|
23
dom/plugins/test/testaddon/Makefile.in
Normal file
23
dom/plugins/test/testaddon/Makefile.in
Normal file
@ -0,0 +1,23 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
plugin_file_name = Test.plugin
|
||||
addon_file_name = testaddon_$(TARGET_XPCOM_ABI).xpi
|
||||
else
|
||||
plugin_file_name = $(DLL_PREFIX)nptest$(DLL_SUFFIX)
|
||||
addon_file_name = testaddon.xpi
|
||||
endif
|
||||
|
||||
# This is so hacky. Waiting on bug 988938.
|
||||
testdir = $(abspath $(DEPTH)/_tests/xpcshell/dom/plugins/test/unit/)
|
||||
addonpath = $(testdir)/$(addon_file_name)
|
||||
|
||||
libs::
|
||||
$(NSINSTALL) -D $(testdir)
|
||||
rm -f $(addonpath)
|
||||
cd $(srcdir) && zip -rD $(addonpath) install.rdf
|
||||
cd $(DIST) && zip -rD $(addonpath) plugins/$(plugin_file_name)
|
23
dom/plugins/test/testaddon/install.rdf
Normal file
23
dom/plugins/test/testaddon/install.rdf
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>test-plugin-from-xpi@tests.mozilla.org</em:id>
|
||||
<em:version>1</em:version>
|
||||
<em:name>Test plugin from XPI</em:name>
|
||||
<em:description>This tests shipping a plugin through an extensions.</em:description>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>toolkit@mozilla.org</em:id>
|
||||
<em:minVersion>0</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<em:unpack>true</em:unpack>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
5
dom/plugins/test/testaddon/moz.build
Normal file
5
dom/plugins/test/testaddon/moz.build
Normal file
@ -0,0 +1,5 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
@ -149,6 +149,7 @@ static bool getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCoun
|
||||
static bool asyncCallbackTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool stallPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool callOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
static bool reinitWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
|
||||
@ -213,6 +214,7 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = {
|
||||
"asyncCallbackTest",
|
||||
"checkGCRace",
|
||||
"hang",
|
||||
"stall",
|
||||
"getClipboardText",
|
||||
"callOnDestroy",
|
||||
"reinitWidget",
|
||||
@ -278,6 +280,7 @@ static const ScriptableFunction sPluginMethodFunctions[] = {
|
||||
asyncCallbackTest,
|
||||
checkGCRace,
|
||||
hangPlugin,
|
||||
stallPlugin,
|
||||
getClipboardText,
|
||||
callOnDestroy,
|
||||
reinitWidget,
|
||||
@ -3355,6 +3358,24 @@ hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
stallPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount,
|
||||
NPVariant* result)
|
||||
{
|
||||
uint32_t stallTimeSeconds = 0;
|
||||
if ((argCount == 1) && NPVARIANT_IS_INT32(args[0])) {
|
||||
stallTimeSeconds = (uint32_t) NPVARIANT_TO_INT32(args[0]);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
Sleep(stallTimeSeconds * 1000U);
|
||||
#else
|
||||
sleep(stallTimeSeconds);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
bool
|
||||
getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount,
|
||||
|
@ -3,13 +3,15 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
|
||||
const gIsOSX = ("nsILocalFileMac" in Ci);
|
||||
const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
|
||||
("@mozilla.org/gio-service;1" in Cc);
|
||||
const gDirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
|
||||
// Finds the test plugin library
|
||||
function get_test_plugin() {
|
||||
@ -42,7 +44,7 @@ function get_test_plugin() {
|
||||
}
|
||||
|
||||
// Finds the test nsIPluginTag
|
||||
function get_test_plugintag(aName) {
|
||||
function get_test_plugintag(aName="Test Plug-in") {
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
@ -119,3 +121,79 @@ function get_test_plugin_no_symlink() {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let gGlobalScope = this;
|
||||
function loadAddonManager() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/Services.jsm", ns);
|
||||
let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
|
||||
let file = do_get_file(head);
|
||||
let uri = ns.Services.io.newFileURI(file);
|
||||
ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
startupManager();
|
||||
}
|
||||
|
||||
// Install addon and return a Promise<boolean> that is
|
||||
// resolve with true on success, false otherwise.
|
||||
function installAddon(relativePath) {
|
||||
let deferred = Promise.defer();
|
||||
let success = () => deferred.resolve(true);
|
||||
let fail = () => deferred.resolve(false);
|
||||
let listener = {
|
||||
onDownloadCancelled: fail,
|
||||
onDownloadFailed: fail,
|
||||
onInstallCancelled: fail,
|
||||
onInstallFailed: fail,
|
||||
onInstallEnded: success,
|
||||
};
|
||||
|
||||
let installCallback = install => {
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
};
|
||||
|
||||
let file = do_get_file(relativePath, false);
|
||||
AddonManager.getInstallForFile(file, installCallback,
|
||||
"application/x-xpinstall");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Uninstall addon and return a Promise<boolean> that is
|
||||
// resolve with true on success, false otherwise.
|
||||
function uninstallAddon(id) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getAddonByID(id, addon => {
|
||||
if (!addon) {
|
||||
deferred.resolve(false);
|
||||
}
|
||||
|
||||
let listener = {};
|
||||
let handler = addon => {
|
||||
if (addon.id !== id) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve(true);
|
||||
};
|
||||
|
||||
listener.onUninstalled = handler;
|
||||
listener.onDisabled = handler;
|
||||
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Returns a Promise<Addon> that is resolved with
|
||||
// the corresponding addon or rejected.
|
||||
function getAddonByID(id) {
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getAddonByID(id, addon => deferred.resolve(addon));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
108
dom/plugins/test/unit/test_plugin_default_state_xpi.js
Normal file
108
dom/plugins/test/unit/test_plugin_default_state_xpi.js
Normal file
@ -0,0 +1,108 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
const ADDON_ID = "test-plugin-from-xpi@tests.mozilla.org";
|
||||
const XRE_EXTENSIONS_DIR_LIST = "XREExtDL";
|
||||
const NS_APP_PLUGINS_DIR_LIST = "APluginsDL";
|
||||
|
||||
const gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
const gXPCOMABI = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).XPCOMABI;
|
||||
let gProfileDir = null;
|
||||
|
||||
function getAddonRoot(profileDir, id) {
|
||||
let dir = profileDir.clone();
|
||||
dir.append("extensions");
|
||||
Assert.ok(dir.exists(), "Extensions dir should exist: " + dir.path);
|
||||
dir.append(id);
|
||||
return dir;
|
||||
}
|
||||
|
||||
function getTestaddonFilename() {
|
||||
let abiPart = "";
|
||||
if (gIsOSX) {
|
||||
abiPart = "_" + gXPCOMABI;
|
||||
}
|
||||
return "testaddon" + abiPart + ".xpi";
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
do_register_cleanup(() => shutdownManager());
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_state() {
|
||||
// Remove test so we will have only one "Test Plug-in" registered.
|
||||
// xpcshell tests have plugins in per-test profiles, so that's fine.
|
||||
let file = get_test_plugin();
|
||||
file.remove(true);
|
||||
|
||||
let success = yield installAddon(getTestaddonFilename());
|
||||
Assert.ok(success, "Should have installed addon.");
|
||||
let addonDir = getAddonRoot(gProfileDir, ADDON_ID);
|
||||
|
||||
let provider = {
|
||||
classID: Components.ID("{0af6b2d7-a06c-49b7-babc-636d292b0dbb}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider,
|
||||
Ci.nsIDirectoryServiceProvider2]),
|
||||
|
||||
getFile: function (prop, persistant) {
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
},
|
||||
|
||||
getFiles: function (prop) {
|
||||
let result = [];
|
||||
|
||||
switch (prop) {
|
||||
case XRE_EXTENSIONS_DIR_LIST:
|
||||
result.push(addonDir);
|
||||
break;
|
||||
case NS_APP_PLUGINS_DIR_LIST:
|
||||
let pluginDir = addonDir.clone();
|
||||
pluginDir.append("plugins");
|
||||
result.push(pluginDir);
|
||||
break;
|
||||
default:
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
|
||||
hasMoreElements: () => result.length > 0,
|
||||
getNext: () => result.shift(),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
||||
|
||||
// We installed a non-restartless addon, need to restart the manager.
|
||||
restartManager();
|
||||
gPluginHost.reloadPlugins();
|
||||
|
||||
Assert.ok(addonDir.exists(), "Addon path should exist: " + addonDir.path);
|
||||
Assert.ok(addonDir.isDirectory(), "Addon path should be a directory: " + addonDir.path);
|
||||
let pluginDir = addonDir.clone();
|
||||
pluginDir.append("plugins");
|
||||
Assert.ok(pluginDir.exists(), "Addon plugins path should exist: " + pluginDir.path);
|
||||
Assert.ok(pluginDir.isDirectory(), "Addon plugins path should be a directory: " + pluginDir.path);
|
||||
|
||||
let addon = yield getAddonByID(ADDON_ID);
|
||||
Assert.ok(!addon.appDisabled, "Addon should not be appDisabled");
|
||||
Assert.ok(addon.isActive, "Addon should be active");
|
||||
Assert.ok(addon.isCompatible, "Addon should be compatible");
|
||||
Assert.ok(!addon.userDisabled, "Addon should not be user disabled");
|
||||
|
||||
let testPlugin = get_test_plugintag();
|
||||
Assert.notEqual(testPlugin, null, "Test plugin should have been found");
|
||||
Assert.equal(testPlugin.enabledState, Ci.nsIPluginTag.STATE_ENABLED, "Test plugin from addon should have state enabled");
|
||||
|
||||
pluginDir.append(testPlugin.filename);
|
||||
Assert.ok(pluginDir.exists(), "Plugin file should exist in addon directory: " + pluginDir.path);
|
||||
});
|
@ -17,3 +17,4 @@ fail-if = os == "android"
|
||||
[test_persist_in_prefs.js]
|
||||
[test_bug854467.js]
|
||||
[test_plugin_default_state.js]
|
||||
[test_plugin_default_state_xpi.js]
|
||||
|
@ -70,6 +70,7 @@ const CDMAICCINFO_CID =
|
||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
||||
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
|
||||
const kNetworkConnStateChangedTopic = "network-connection-state-changed";
|
||||
const kNetworkActiveChangedTopic = "network-active-changed";
|
||||
const kSmsReceivedObserverTopic = "sms-received";
|
||||
const kSilentSmsReceivedObserverTopic = "silent-sms-received";
|
||||
const kSmsSendingObserverTopic = "sms-sending";
|
||||
@ -1392,12 +1393,6 @@ DataConnectionHandler.prototype = {
|
||||
// necessary data call and interface information to RILContentHelper
|
||||
// and network manager for current data call type.
|
||||
if (apnSetting.iface.connected) {
|
||||
if (apnType == "default" && !dataInfo.connected) {
|
||||
dataInfo.connected = true;
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, dataInfo);
|
||||
}
|
||||
|
||||
// Update the interface status via-registration if the interface has
|
||||
// already been registered in the network manager.
|
||||
if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
|
||||
@ -1431,13 +1426,6 @@ DataConnectionHandler.prototype = {
|
||||
// necessary data call and interface information to RILContentHelper
|
||||
// and network manager for current data call type.
|
||||
if (apnSetting.iface.connectedTypes.length && apnSetting.iface.connected) {
|
||||
let dataInfo = this.radioInterface.rilContext.data;
|
||||
if (apnType == "default" && dataInfo.connected) {
|
||||
dataInfo.connected = false;
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, dataInfo);
|
||||
}
|
||||
|
||||
// Update the interface status via-registration if the interface has
|
||||
// already been registered in the network manager.
|
||||
if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
|
||||
@ -1511,25 +1499,6 @@ DataConnectionHandler.prototype = {
|
||||
* Handle data call state changes.
|
||||
*/
|
||||
handleDataCallState: function(datacall) {
|
||||
let data = this.radioInterface.rilContext.data;
|
||||
let defaultApnSetting = this.apnSettings && this.apnSettings.byType.default;
|
||||
let dataCallConnected =
|
||||
(datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED);
|
||||
if (defaultApnSetting && datacall.ifname) {
|
||||
if (dataCallConnected && datacall.apn == defaultApnSetting.apn &&
|
||||
defaultApnSetting.iface.inConnectedTypes("default")) {
|
||||
data.connected = dataCallConnected;
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, data);
|
||||
data.apn = datacall.apn;
|
||||
} else if (!dataCallConnected && datacall.apn == data.apn) {
|
||||
data.connected = dataCallConnected;
|
||||
delete data.apn;
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, data);
|
||||
}
|
||||
}
|
||||
|
||||
this._deliverDataCallCallback("dataCallStateChanged", [datacall]);
|
||||
|
||||
// Process pending radio power off request after all data calls
|
||||
@ -1878,6 +1847,7 @@ function RadioInterface(aClientId, aWorkerMessenger) {
|
||||
Services.obs.addObserver(this, kScreenStateChangedTopic, false);
|
||||
|
||||
Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
|
||||
Services.obs.addObserver(this, kNetworkActiveChangedTopic, false);
|
||||
Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false);
|
||||
|
||||
this.portAddressedSmsApps = {};
|
||||
@ -1919,6 +1889,7 @@ RadioInterface.prototype = {
|
||||
Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
|
||||
Services.obs.removeObserver(this, kScreenStateChangedTopic);
|
||||
Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
|
||||
Services.obs.removeObserver(this, kNetworkActiveChangedTopic);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -2450,13 +2421,12 @@ RadioInterface.prototype = {
|
||||
dataInfo.type = newInfo.type;
|
||||
// For the data connection, the `connected` flag indicates whether
|
||||
// there's an active data call.
|
||||
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
|
||||
let apnSettings = connHandler.apnSettings;
|
||||
let apnSetting = apnSettings && apnSettings.byType.default;
|
||||
dataInfo.connected = false;
|
||||
if (apnSetting) {
|
||||
dataInfo.connected = (connHandler.getDataCallStateByType("default") ==
|
||||
RIL.GECKO_NETWORK_STATE_CONNECTED);
|
||||
if (gNetworkManager.active &&
|
||||
gNetworkManager.active.type ===
|
||||
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
|
||||
gNetworkManager.active.serviceId === this.clientId) {
|
||||
dataInfo.connected = true;
|
||||
}
|
||||
|
||||
// Make sure we also reset the operator and signal strength information
|
||||
@ -2475,6 +2445,8 @@ RadioInterface.prototype = {
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, dataInfo);
|
||||
}
|
||||
|
||||
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
|
||||
connHandler.updateRILNetworkInterface();
|
||||
},
|
||||
|
||||
@ -3364,6 +3336,21 @@ RadioInterface.prototype = {
|
||||
this._sntp.request();
|
||||
}
|
||||
break;
|
||||
case kNetworkActiveChangedTopic:
|
||||
let dataInfo = this.rilContext.data;
|
||||
let connected = false;
|
||||
if (gNetworkManager.active &&
|
||||
gNetworkManager.active.type ===
|
||||
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
|
||||
gNetworkManager.active.serviceId === this.clientId) {
|
||||
connected = true;
|
||||
}
|
||||
if (dataInfo.connected !== connected) {
|
||||
dataInfo.connected = connected;
|
||||
gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
|
||||
this.clientId, dataInfo);
|
||||
}
|
||||
break;
|
||||
case kScreenStateChangedTopic:
|
||||
this.workerMessenger.send("setScreenState", { on: (data === "on") });
|
||||
break;
|
||||
|
@ -366,6 +366,7 @@ private:
|
||||
GROUP3_OP_TEST = 0,
|
||||
GROUP3_OP_NOT = 2,
|
||||
GROUP3_OP_NEG = 3,
|
||||
GROUP3_OP_IMUL = 5,
|
||||
GROUP3_OP_DIV = 6,
|
||||
GROUP3_OP_IDIV = 7,
|
||||
|
||||
@ -1155,6 +1156,13 @@ public:
|
||||
m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src);
|
||||
}
|
||||
|
||||
void imull_r(RegisterID multiplier)
|
||||
{
|
||||
spew("imull %s",
|
||||
nameIReg(4, multiplier));
|
||||
m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IMUL, multiplier);
|
||||
}
|
||||
|
||||
void imull_mr(int offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("imull %s0x%x(%s), %s",
|
||||
|
19
js/src/jit-test/tests/basic/testDivModWithIntMin.js
Normal file
19
js/src/jit-test/tests/basic/testDivModWithIntMin.js
Normal file
@ -0,0 +1,19 @@
|
||||
var intMin = -2147483648;
|
||||
|
||||
assertEq(intMin % (-2147483648), -0);
|
||||
assertEq(intMin % (-3), -2);
|
||||
assertEq(intMin % (-1), -0);
|
||||
assertEq(intMin % 1, -0);
|
||||
assertEq(intMin % 3, -2);
|
||||
assertEq(intMin % 10, -8);
|
||||
assertEq(intMin % 2147483647, -1);
|
||||
|
||||
assertEq((-2147483648) % (-2147483648), -0);
|
||||
for (var i = -10; i <= 10; ++i)
|
||||
assertEq(i % (-2147483648), i);
|
||||
assertEq(2147483647 % (-2147483648), 2147483647);
|
||||
|
||||
assertEq((-2147483648) / (-2147483648), 1);
|
||||
assertEq(0 / (-2147483648), -0);
|
||||
assertEq((-2147483648) / (-1), 2147483648);
|
||||
assertEq((-0) / (-2147483648), 0);
|
@ -1339,9 +1339,10 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
SnapshotIterator snapIter(iter);
|
||||
|
||||
RootedFunction callee(cx, iter.maybeCallee());
|
||||
RootedScript scr(cx, iter.script());
|
||||
if (callee) {
|
||||
IonSpew(IonSpew_BaselineBailouts, " Callee function (%s:%u)",
|
||||
callee->existingScript()->filename(), callee->existingScript()->lineno());
|
||||
scr->filename(), scr->lineno());
|
||||
} else {
|
||||
IonSpew(IonSpew_BaselineBailouts, " No callee!");
|
||||
}
|
||||
@ -1358,7 +1359,6 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
RootedScript caller(cx);
|
||||
jsbytecode *callerPC = nullptr;
|
||||
RootedFunction fun(cx, callee);
|
||||
RootedScript scr(cx, iter.script());
|
||||
AutoValueVector startFrameFormals(cx);
|
||||
|
||||
RootedScript topCaller(cx);
|
||||
@ -1400,7 +1400,7 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
caller = scr;
|
||||
callerPC = callPC;
|
||||
fun = nextCallee;
|
||||
scr = fun->existingScript();
|
||||
scr = fun->existingScriptForInlinedFunction();
|
||||
|
||||
// Save top caller info for adjusting SPS frames later.
|
||||
if (!topCaller) {
|
||||
|
@ -1652,7 +1652,7 @@ InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
|
||||
// Inlined functions may be clones that still point to the lazy script
|
||||
// for the executed script, if they are clones. The actual script
|
||||
// exists though, just make sure the function points to it.
|
||||
script_ = callee_->existingScript();
|
||||
script_ = callee_->existingScriptForInlinedFunction();
|
||||
MOZ_ASSERT(script_->hasBaselineScript());
|
||||
|
||||
pc_ = script_->offsetToPC(si_.pcOffset());
|
||||
|
@ -1062,12 +1062,18 @@ class AssemblerX86Shared
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void imull(const Register &multiplier) {
|
||||
masm.imull_r(multiplier.code());
|
||||
}
|
||||
void imull(Imm32 imm, const Register &dest) {
|
||||
masm.imull_i32r(dest.code(), imm.value, dest.code());
|
||||
}
|
||||
void imull(const Register &src, const Register &dest) {
|
||||
masm.imull_rr(src.code(), dest.code());
|
||||
}
|
||||
void imull(Imm32 imm, const Register &src, const Register &dest) {
|
||||
masm.imull_i32r(src.code(), imm.value, dest.code());
|
||||
}
|
||||
void imull(const Operand &src, const Register &dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::REG:
|
||||
|
@ -1007,6 +1007,74 @@ CodeGeneratorShared::addCacheLocations(const CacheLocationList &locs, size_t *nu
|
||||
return firstIndex;
|
||||
}
|
||||
|
||||
ReciprocalMulConstants
|
||||
CodeGeneratorShared::computeDivisionConstants(int d) {
|
||||
// In what follows, d is positive and is not a power of 2.
|
||||
JS_ASSERT(d > 0 && (d & (d - 1)) != 0);
|
||||
|
||||
// Speeding up division by non power-of-2 constants is possible by
|
||||
// calculating, during compilation, a value M such that high-order
|
||||
// bits of M*n correspond to the result of the division. Formally,
|
||||
// we compute values 0 <= M < 2^32 and 0 <= s < 31 such that
|
||||
// (M * n) >> (32 + s) = floor(n/d) if n >= 0
|
||||
// (M * n) >> (32 + s) = ceil(n/d) - 1 if n < 0.
|
||||
// The original presentation of this technique appears in Hacker's
|
||||
// Delight, a book by Henry S. Warren, Jr.. A proof of correctness
|
||||
// for our version follows.
|
||||
|
||||
// Define p = 32 + s, M = ceil(2^p/d), and assume that s satisfies
|
||||
// M - 2^p/d <= 2^(s+1)/d. (1)
|
||||
// (Observe that s = FloorLog32(d) satisfies this, because in this
|
||||
// case d <= 2^(s+1) and so the RHS of (1) is at least one). Then,
|
||||
//
|
||||
// a) If s <= FloorLog32(d), then M <= 2^32 - 1.
|
||||
// Proof: Indeed, M is monotone in s and, for s = FloorLog32(d),
|
||||
// the inequalities 2^31 > d >= 2^s + 1 readily imply
|
||||
// 2^p / d = 2^p/(d - 1) * (d - 1)/d
|
||||
// <= 2^32 * (1 - 1/d) < 2 * (2^31 - 1) = 2^32 - 2.
|
||||
// The claim follows by applying the ceiling function.
|
||||
//
|
||||
// b) For any 0 <= n < 2^31, floor(Mn/2^p) = floor(n/d).
|
||||
// Proof: Put x = floor(Mn/2^p); it's the unique integer for which
|
||||
// Mn/2^p - 1 < x <= Mn/2^p. (2)
|
||||
// Using M >= 2^p/d on the LHS and (1) on the RHS, we get
|
||||
// n/d - 1 < x <= n/d + n/(2^31 d) < n/d + 1/d.
|
||||
// Since x is an integer, it's not in the interval (n/d, (n+1)/d),
|
||||
// and so n/d - 1 < x <= n/d, which implies x = floor(n/d).
|
||||
//
|
||||
// c) For any -2^31 <= n < 0, floor(Mn/2^p) + 1 = ceil(n/d).
|
||||
// Proof: The proof is similar. Equation (2) holds as above. Using
|
||||
// M > 2^p/d (d isn't a power of 2) on the RHS and (1) on the LHS,
|
||||
// n/d + n/(2^31 d) - 1 < x < n/d.
|
||||
// Using n >= -2^31 and summing 1,
|
||||
// n/d - 1/d < x + 1 < n/d + 1.
|
||||
// Since x + 1 is an integer, this implies n/d <= x + 1 < n/d + 1.
|
||||
// In other words, x + 1 = ceil(n/d).
|
||||
//
|
||||
// Condition (1) isn't necessary for the existence of M and s with
|
||||
// the properties above. Hacker's Delight provides a slightly less
|
||||
// restrictive condition when d >= 196611, at the cost of a 3-page
|
||||
// proof of correctness.
|
||||
|
||||
// Note that, since d*M - 2^p = d - (2^p)%d, (1) can be written as
|
||||
// 2^(s+1) >= d - (2^p)%d.
|
||||
// We now compute the least s with this property...
|
||||
|
||||
int32_t shift = 0;
|
||||
while ((int64_t(1) << (shift+1)) + (int64_t(1) << (shift+32)) % d < d)
|
||||
shift++;
|
||||
|
||||
// ...and the corresponding M. This may not fit in a signed 32-bit
|
||||
// integer; we will compute (M - 2^32) * n + (2^32 * n) instead of
|
||||
// M * n if this is the case (cf. item (a) above).
|
||||
ReciprocalMulConstants rmc;
|
||||
rmc.multiplier = int32_t((int64_t(1) << (shift+32))/d + 1);
|
||||
rmc.shift_amount = shift;
|
||||
|
||||
return rmc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
|
||||
bool
|
||||
|
@ -45,6 +45,11 @@ struct PatchableBackedgeInfo
|
||||
{}
|
||||
};
|
||||
|
||||
struct ReciprocalMulConstants {
|
||||
int32_t multiplier;
|
||||
int32_t shift_amount;
|
||||
};
|
||||
|
||||
class CodeGeneratorShared : public LInstructionVisitor
|
||||
{
|
||||
js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
|
||||
@ -402,6 +407,7 @@ class CodeGeneratorShared : public LInstructionVisitor
|
||||
|
||||
bool addCache(LInstruction *lir, size_t cacheIndex);
|
||||
size_t addCacheLocations(const CacheLocationList &locs, size_t *numLocs);
|
||||
ReciprocalMulConstants computeDivisionConstants(int d);
|
||||
|
||||
protected:
|
||||
bool addOutOfLineCode(OutOfLineCode *code);
|
||||
|
@ -22,6 +22,7 @@
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
using mozilla::Abs;
|
||||
using mozilla::FloatingPoint;
|
||||
using mozilla::FloorLog2;
|
||||
using mozilla::NegativeInfinity;
|
||||
@ -869,14 +870,23 @@ CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins)
|
||||
{
|
||||
Register lhs = ToRegister(ins->numerator());
|
||||
mozilla::DebugOnly<Register> output = ToRegister(ins->output());
|
||||
|
||||
int32_t shift = ins->shift();
|
||||
bool negativeDivisor = ins->negativeDivisor();
|
||||
MDiv *mir = ins->mir();
|
||||
|
||||
// We use defineReuseInput so these should always be the same, which is
|
||||
// convenient since all of our instructions here are two-address.
|
||||
JS_ASSERT(lhs == output);
|
||||
|
||||
if (!mir->isTruncated() && negativeDivisor) {
|
||||
// 0 divided by a negative number must return a double.
|
||||
masm.testl(lhs, lhs);
|
||||
if (!bailoutIf(Assembler::Zero, ins->snapshot()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shift != 0) {
|
||||
MDiv *mir = ins->mir();
|
||||
if (!mir->isTruncated()) {
|
||||
// If the remainder is != 0, bailout since this must be a double.
|
||||
masm.testl(lhs, Imm32(UINT32_MAX >> (32 - shift)));
|
||||
@ -884,24 +894,108 @@ CodeGeneratorX86Shared::visitDivPowTwoI(LDivPowTwoI *ins)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mir->canBeNegativeDividend()) {
|
||||
// Numerator is unsigned, so needs no adjusting. Do the shift.
|
||||
masm.sarl(Imm32(shift), lhs);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adjust the value so that shifting produces a correctly rounded result
|
||||
// when the numerator is negative. See 10-1 "Signed Division by a Known
|
||||
// Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
|
||||
if (mir->canBeNegativeDividend()) {
|
||||
Register lhsCopy = ToRegister(ins->numeratorCopy());
|
||||
JS_ASSERT(lhsCopy != lhs);
|
||||
if (shift > 1)
|
||||
masm.sarl(Imm32(31), lhs);
|
||||
masm.shrl(Imm32(32 - shift), lhs);
|
||||
masm.addl(lhsCopy, lhs);
|
||||
}
|
||||
|
||||
// Do the shift.
|
||||
masm.sarl(Imm32(shift), lhs);
|
||||
if (negativeDivisor)
|
||||
masm.negl(lhs);
|
||||
} else if (shift == 0 && negativeDivisor) {
|
||||
// INT32_MIN / -1 overflows.
|
||||
masm.negl(lhs);
|
||||
if (!mir->isTruncated() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI *ins) {
|
||||
Register lhs = ToRegister(ins->numerator());
|
||||
Register output = ToRegister(ins->output());
|
||||
int32_t d = ins->denominator();
|
||||
|
||||
// This emits the division answer into edx or the modulus answer into eax.
|
||||
JS_ASSERT(output == eax || output == edx);
|
||||
JS_ASSERT(lhs != eax && lhs != edx);
|
||||
|
||||
// The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI
|
||||
// and LModPowTwoI).
|
||||
JS_ASSERT((Abs(d) & (Abs(d) - 1)) != 0);
|
||||
|
||||
// We will first divide by Abs(d), and negate the answer if d is negative.
|
||||
// If desired, this can be avoided by generalizing computeDivisionConstants.
|
||||
ReciprocalMulConstants rmc = computeDivisionConstants(Abs(d));
|
||||
|
||||
// As explained in the comments of computeDivisionConstants, we first compute
|
||||
// X >> (32 + shift), where X is either (rmc.multiplier * n) if the multiplier
|
||||
// is non-negative or (rmc.multiplier * n) + (2^32 * n) otherwise. This is the
|
||||
// desired division result if n is non-negative, and is one less than the result
|
||||
// otherwise.
|
||||
masm.movl(lhs, eax);
|
||||
masm.movl(Imm32(rmc.multiplier), edx);
|
||||
masm.imull(edx);
|
||||
if (rmc.multiplier < 0)
|
||||
masm.addl(lhs, edx);
|
||||
masm.sarl(Imm32(rmc.shift_amount), edx);
|
||||
|
||||
// We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be
|
||||
// computed with just a sign-extending shift of 31 bits.
|
||||
if (ins->canBeNegativeDividend()) {
|
||||
masm.movl(lhs, eax);
|
||||
masm.sarl(Imm32(31), eax);
|
||||
masm.subl(eax, edx);
|
||||
}
|
||||
|
||||
// After this, edx contains the correct division result.
|
||||
if (d < 0)
|
||||
masm.negl(edx);
|
||||
|
||||
if (output == eax) {
|
||||
masm.imull(Imm32(-d), edx, eax);
|
||||
masm.addl(lhs, eax);
|
||||
}
|
||||
|
||||
if (!ins->mir()->isTruncated()) {
|
||||
if (output == edx) {
|
||||
// This is a division op. Multiply the obtained value by d to check if
|
||||
// the correct answer is an integer. This cannot overflow, since |d| > 1.
|
||||
masm.imull(Imm32(d), edx, eax);
|
||||
masm.cmpl(lhs, eax);
|
||||
if (!bailoutIf(Assembler::NotEqual, ins->snapshot()))
|
||||
return false;
|
||||
|
||||
// If lhs is zero and the divisor is negative, the answer should have
|
||||
// been -0.
|
||||
if (d < 0) {
|
||||
masm.testl(lhs, lhs);
|
||||
if (!bailoutIf(Assembler::Zero, ins->snapshot()))
|
||||
return false;
|
||||
}
|
||||
} else if (ins->canBeNegativeDividend()) {
|
||||
// This is a mod op. If the computed value is zero and lhs
|
||||
// is negative, the answer should have been -0.
|
||||
Label done;
|
||||
|
||||
masm.cmpl(lhs, Imm32(0));
|
||||
masm.j(Assembler::GreaterThanOrEqual, &done);
|
||||
|
||||
masm.testl(eax, eax);
|
||||
if (!bailoutIf(Assembler::Zero, ins->snapshot()))
|
||||
return false;
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1012,7 +1106,7 @@ CodeGeneratorX86Shared::visitModPowTwoI(LModPowTwoI *ins)
|
||||
masm.branchTest32(Assembler::Signed, lhs, lhs, &negative);
|
||||
}
|
||||
|
||||
masm.andl(Imm32((1 << shift) - 1), lhs);
|
||||
masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
|
||||
|
||||
if (ins->mir()->canBeNegativeDividend()) {
|
||||
Label done;
|
||||
@ -1020,11 +1114,19 @@ CodeGeneratorX86Shared::visitModPowTwoI(LModPowTwoI *ins)
|
||||
|
||||
// Negative numbers need a negate, bitmask, negate
|
||||
masm.bind(&negative);
|
||||
// visitModI has an overflow check here to catch INT_MIN % -1, but
|
||||
// here the rhs is a power of 2, and cannot be -1, so the check is not generated.
|
||||
|
||||
// Unlike in the visitModI case, we are not computing the mod by means of a
|
||||
// division. Therefore, the divisor = -1 case isn't problematic (the andl
|
||||
// always returns 0, which is what we expect).
|
||||
//
|
||||
// The negl instruction overflows if lhs == INT32_MIN, but this is also not
|
||||
// a problem: shift is at most 31, and so the andl also always returns 0.
|
||||
masm.negl(lhs);
|
||||
masm.andl(Imm32((1 << shift) - 1), lhs);
|
||||
masm.andl(Imm32((uint32_t(1) << shift) - 1), lhs);
|
||||
masm.negl(lhs);
|
||||
|
||||
// Since a%b has the same sign as b, and a is negative in this branch,
|
||||
// an answer of 0 means the correct result is actually -0. Bail out.
|
||||
if (!ins->mir()->isTruncated() && !bailoutIf(Assembler::Zero, ins->snapshot()))
|
||||
return false;
|
||||
masm.bind(&done);
|
||||
|
@ -124,6 +124,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
virtual bool visitMulI(LMulI *ins);
|
||||
virtual bool visitDivI(LDivI *ins);
|
||||
virtual bool visitDivPowTwoI(LDivPowTwoI *ins);
|
||||
virtual bool visitDivOrModConstantI(LDivOrModConstantI *ins);
|
||||
virtual bool visitModI(LModI *ins);
|
||||
virtual bool visitModPowTwoI(LModPowTwoI *ins);
|
||||
virtual bool visitBitNotI(LBitNotI *ins);
|
||||
|
@ -47,12 +47,13 @@ class LDivI : public LBinaryMath<1>
|
||||
class LDivPowTwoI : public LBinaryMath<0>
|
||||
{
|
||||
const int32_t shift_;
|
||||
const bool negativeDivisor_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(DivPowTwoI)
|
||||
|
||||
LDivPowTwoI(const LAllocation &lhs, const LAllocation &lhsCopy, int32_t shift)
|
||||
: shift_(shift)
|
||||
LDivPowTwoI(const LAllocation &lhs, const LAllocation &lhsCopy, int32_t shift, bool negativeDivisor)
|
||||
: shift_(shift), negativeDivisor_(negativeDivisor)
|
||||
{
|
||||
setOperand(0, lhs);
|
||||
setOperand(1, lhsCopy);
|
||||
@ -67,11 +68,45 @@ class LDivPowTwoI : public LBinaryMath<0>
|
||||
int32_t shift() const {
|
||||
return shift_;
|
||||
}
|
||||
bool negativeDivisor() const {
|
||||
return negativeDivisor_;
|
||||
}
|
||||
MDiv *mir() const {
|
||||
return mir_->toDiv();
|
||||
}
|
||||
};
|
||||
|
||||
class LDivOrModConstantI : public LInstructionHelper<1, 1, 1>
|
||||
{
|
||||
const int32_t denominator_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(DivOrModConstantI)
|
||||
|
||||
LDivOrModConstantI(const LAllocation &lhs, int32_t denominator, const LDefinition& temp)
|
||||
: denominator_(denominator)
|
||||
{
|
||||
setOperand(0, lhs);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *numerator() {
|
||||
return getOperand(0);
|
||||
}
|
||||
int32_t denominator() const {
|
||||
return denominator_;
|
||||
}
|
||||
MBinaryArithInstruction *mir() const {
|
||||
JS_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||
return static_cast<MBinaryArithInstruction *>(mir_);
|
||||
}
|
||||
bool canBeNegativeDividend() const {
|
||||
if (mir_->isMod())
|
||||
return mir_->toMod()->canBeNegativeDividend();
|
||||
return mir_->toDiv()->canBeNegativeDividend();
|
||||
}
|
||||
};
|
||||
|
||||
class LModI : public LBinaryMath<1>
|
||||
{
|
||||
public:
|
||||
|
@ -15,6 +15,7 @@
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
using mozilla::Abs;
|
||||
using mozilla::FloorLog2;
|
||||
|
||||
LTableSwitch *
|
||||
@ -137,26 +138,29 @@ LIRGeneratorX86Shared::lowerDivI(MDiv *div)
|
||||
if (div->rhs()->isConstant()) {
|
||||
int32_t rhs = div->rhs()->toConstant()->value().toInt32();
|
||||
|
||||
// Check for division by a positive power of two, which is an easy and
|
||||
// important case to optimize. Note that other optimizations are also
|
||||
// possible; division by negative powers of two can be optimized in a
|
||||
// similar manner as positive powers of two, and division by other
|
||||
// constants can be optimized by a reciprocal multiplication technique.
|
||||
int32_t shift = FloorLog2(rhs);
|
||||
if (rhs > 0 && 1 << shift == rhs) {
|
||||
// Division by powers of two can be done by shifting, and division by
|
||||
// other numbers can be done by a reciprocal multiplication technique.
|
||||
int32_t shift = FloorLog2(Abs(rhs));
|
||||
if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) {
|
||||
LAllocation lhs = useRegisterAtStart(div->lhs());
|
||||
LDivPowTwoI *lir;
|
||||
if (!div->canBeNegativeDividend()) {
|
||||
// Numerator is unsigned, so does not need adjusting.
|
||||
lir = new(alloc()) LDivPowTwoI(lhs, lhs, shift);
|
||||
lir = new(alloc()) LDivPowTwoI(lhs, lhs, shift, rhs < 0);
|
||||
} else {
|
||||
// Numerator is signed, and needs adjusting, and an extra
|
||||
// lhs copy register is needed.
|
||||
lir = new(alloc()) LDivPowTwoI(lhs, useRegister(div->lhs()), shift);
|
||||
lir = new(alloc()) LDivPowTwoI(lhs, useRegister(div->lhs()), shift, rhs < 0);
|
||||
}
|
||||
if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo))
|
||||
return false;
|
||||
return defineReuseInput(lir, div, 0);
|
||||
} else if (rhs != 0) {
|
||||
LDivOrModConstantI *lir;
|
||||
lir = new(alloc()) LDivOrModConstantI(useRegister(div->lhs()), rhs, tempFixed(eax));
|
||||
if (div->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo))
|
||||
return false;
|
||||
return defineFixed(lir, div, LAllocation(AnyRegister(edx)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,12 +179,18 @@ LIRGeneratorX86Shared::lowerModI(MMod *mod)
|
||||
|
||||
if (mod->rhs()->isConstant()) {
|
||||
int32_t rhs = mod->rhs()->toConstant()->value().toInt32();
|
||||
int32_t shift = FloorLog2(rhs);
|
||||
if (rhs > 0 && 1 << shift == rhs) {
|
||||
int32_t shift = FloorLog2(Abs(rhs));
|
||||
if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) {
|
||||
LModPowTwoI *lir = new(alloc()) LModPowTwoI(useRegisterAtStart(mod->lhs()), shift);
|
||||
if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo))
|
||||
return false;
|
||||
return defineReuseInput(lir, mod, 0);
|
||||
} else if (rhs != 0) {
|
||||
LDivOrModConstantI *lir;
|
||||
lir = new(alloc()) LDivOrModConstantI(useRegister(mod->lhs()), rhs, tempFixed(edx));
|
||||
if (mod->fallible() && !assignSnapshot(lir, Bailout_BaselineInfo))
|
||||
return false;
|
||||
return defineFixed(lir, mod, LAllocation(AnyRegister(eax)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
_(UnboxFloatingPoint) \
|
||||
_(DivI) \
|
||||
_(DivPowTwoI) \
|
||||
_(DivOrModConstantI) \
|
||||
_(ModI) \
|
||||
_(ModPowTwoI) \
|
||||
_(PowHalfD) \
|
||||
|
@ -14,6 +14,7 @@
|
||||
_(BoxFloatingPoint) \
|
||||
_(DivI) \
|
||||
_(DivPowTwoI) \
|
||||
_(DivOrModConstantI) \
|
||||
_(ModI) \
|
||||
_(ModPowTwoI) \
|
||||
_(PowHalfD) \
|
||||
|
@ -268,9 +268,9 @@ class JSFunction : public JSObject
|
||||
// JSScript, delazifying the function if necessary. This is the safest to
|
||||
// use, but has extra checks, requires a cx and may trigger a GC.
|
||||
//
|
||||
// - For functions which may have a LazyScript but whose JSScript is known
|
||||
// to exist, existingScript() will get the script and delazify the
|
||||
// function if necessary.
|
||||
// - For inlined functions which may have a LazyScript but whose JSScript
|
||||
// is known to exist, existingScriptForInlinedFunction() will get the
|
||||
// script and delazify the function if necessary.
|
||||
//
|
||||
// - For functions known to have a JSScript, nonLazyScript() will get it.
|
||||
|
||||
@ -286,12 +286,18 @@ class JSFunction : public JSObject
|
||||
return nonLazyScript();
|
||||
}
|
||||
|
||||
JSScript *existingScript() {
|
||||
JS_ASSERT(isInterpreted());
|
||||
JSScript *existingScriptForInlinedFunction() {
|
||||
MOZ_ASSERT(isInterpreted());
|
||||
if (isInterpretedLazy()) {
|
||||
// Get the script from the canonical function. Ion used the
|
||||
// canonical function to inline the script and because it has
|
||||
// Baseline code it has not been relazified. Note that we can't
|
||||
// use lazyScript->script_ here as it may be null in some cases,
|
||||
// see bug 976536.
|
||||
js::LazyScript *lazy = lazyScript();
|
||||
JSScript *script = lazy->maybeScript();
|
||||
JS_ASSERT(script);
|
||||
JSFunction *fun = lazy->functionNonDelazifying();
|
||||
MOZ_ASSERT(fun);
|
||||
JSScript *script = fun->nonLazyScript();
|
||||
|
||||
if (shadowZone()->needsBarrier())
|
||||
js::LazyScript::writeBarrierPre(lazy);
|
||||
|
@ -121,7 +121,7 @@ interface ScheduledGCCallback : nsISupports
|
||||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
[scriptable, uuid(5e253a02-f91f-4fb3-9335-db0fca23f2e0)]
|
||||
[scriptable, uuid(45b80e00-fb0d-439e-b7bf-54f24af0c4a6)]
|
||||
interface nsIXPCComponents_Utils : nsISupports
|
||||
{
|
||||
|
||||
@ -568,6 +568,13 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
* function throws. If it throws, you probably shouldn't be using it.
|
||||
*/
|
||||
nsIPrincipal getWebIDLCallerPrincipal();
|
||||
|
||||
/*
|
||||
* Gets the principal of a script object, after unwrapping any cross-
|
||||
* compartment wrappers.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
nsIPrincipal getObjectPrincipal(in jsval obj);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3257,7 +3257,7 @@ nsXPCComponents_Utils::BlockScriptForGlobal(HandleValue globalArg,
|
||||
RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(),
|
||||
/* stopAtOuter = */ false));
|
||||
NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
|
||||
if (nsContentUtils::IsSystemPrincipal(GetObjectPrincipal(global))) {
|
||||
if (nsContentUtils::IsSystemPrincipal(xpc::GetObjectPrincipal(global))) {
|
||||
JS_ReportError(cx, "Script may not be disabled for system globals");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -3273,7 +3273,7 @@ nsXPCComponents_Utils::UnblockScriptForGlobal(HandleValue globalArg,
|
||||
RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(),
|
||||
/* stopAtOuter = */ false));
|
||||
NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
|
||||
if (nsContentUtils::IsSystemPrincipal(GetObjectPrincipal(global))) {
|
||||
if (nsContentUtils::IsSystemPrincipal(xpc::GetObjectPrincipal(global))) {
|
||||
JS_ReportError(cx, "Script may not be disabled for system globals");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -3616,6 +3616,21 @@ nsXPCComponents_Utils::GetWebIDLCallerPrincipal(nsIPrincipal **aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::GetObjectPrincipal(HandleValue val, JSContext *cx,
|
||||
nsIPrincipal **result)
|
||||
{
|
||||
if (!val.isObject())
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
RootedObject obj(cx, &val.toObject());
|
||||
obj = js::CheckedUnwrap(obj);
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> prin = nsContentUtils::GetObjectPrincipal(obj);
|
||||
prin.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
|
@ -1,6 +1,11 @@
|
||||
function run_test() {
|
||||
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService(
|
||||
Components.interfaces.nsIScriptSecurityManager);
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
do_check_true(secMan.isSystemPrincipal(secMan.getObjectPrincipal({})));
|
||||
function run_test() {
|
||||
var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
|
||||
do_check_true(secMan.isSystemPrincipal(Cu.getObjectPrincipal({})));
|
||||
var sb = new Cu.Sandbox('http://www.example.com');
|
||||
Cu.evalInSandbox('var obj = { foo: 42 };', sb);
|
||||
do_check_eq(Cu.getObjectPrincipal(sb.obj).origin, 'http://www.example.com');
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ bcrook.com: max-age too low: 86400
|
||||
betnet.fr: could not connect to host
|
||||
bigshinylock.minazo.net: could not connect to host
|
||||
blacklane.com: did not receive HSTS header
|
||||
blueseed.co: did not receive HSTS header
|
||||
braintreegateway.com: did not receive HSTS header
|
||||
browserid.org: did not receive HSTS header
|
||||
business.medbank.com.mt: did not receive HSTS header
|
||||
@ -25,7 +26,7 @@ codereview.chromium.org: did not receive HSTS header (error ignored - included r
|
||||
crate.io: did not receive HSTS header
|
||||
crowdcurity.com: did not receive HSTS header
|
||||
crypto.is: did not receive HSTS header
|
||||
csawctf.poly.edu: could not connect to host
|
||||
csawctf.poly.edu: did not receive HSTS header
|
||||
dl.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
docs.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
drive.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
@ -74,12 +75,6 @@ nexth.net: did not receive HSTS header
|
||||
nexth.us: could not connect to host
|
||||
openshift.redhat.com: did not receive HSTS header
|
||||
ottospora.nl: could not connect to host
|
||||
passport.yandex.by: did not receive HSTS header
|
||||
passport.yandex.com: did not receive HSTS header
|
||||
passport.yandex.com.tr: did not receive HSTS header
|
||||
passport.yandex.kz: did not receive HSTS header
|
||||
passport.yandex.ru: did not receive HSTS header
|
||||
passport.yandex.ua: did not receive HSTS header
|
||||
paypal.com: max-age too low: 14400
|
||||
payroll.xero.com: max-age too low: 3600
|
||||
platform.lookout.com: could not connect to host
|
||||
@ -88,7 +83,6 @@ prodpad.com: did not receive HSTS header
|
||||
profiles.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
rapidresearch.me: did not receive HSTS header
|
||||
sah3.net: could not connect to host
|
||||
sandbox.mydigipass.com: could not connect to host
|
||||
saturngames.co.uk: did not receive HSTS header
|
||||
script.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
security.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
@ -105,6 +99,8 @@ spreadsheets.google.com: did not receive HSTS header (error ignored - included r
|
||||
square.com: did not receive HSTS header
|
||||
ssl.google-analytics.com: did not receive HSTS header (error ignored - included regardless)
|
||||
ssl.panoramio.com: did not receive HSTS header
|
||||
stage.wepay.com: max-age too low: 2592000
|
||||
static.wepay.com: did not receive HSTS header
|
||||
sunshinepress.org: could not connect to host
|
||||
surfeasy.com: did not receive HSTS header
|
||||
talk.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
@ -113,6 +109,7 @@ translate.googleapis.com: did not receive HSTS header (error ignored - included
|
||||
uprotect.it: could not connect to host
|
||||
wallet.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
webmail.mayfirst.org: did not receive HSTS header
|
||||
wepay.com: max-age too low: 2592000
|
||||
whonix.org: did not receive HSTS header
|
||||
www.calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-hsts-000000000000000/getHSTSPreloadList.js :: processStsHeader :: line 125" data: no]
|
||||
www.cueup.com: did not receive HSTS header
|
||||
@ -134,4 +131,5 @@ www.paypal.com: [Exception... "Component returned failure code: 0x80004005 (NS_E
|
||||
www.roddis.net: did not receive HSTS header
|
||||
www.sandbox.mydigipass.com: could not connect to host
|
||||
www.surfeasy.com: did not receive HSTS header
|
||||
www.wepay.com: max-age too low: 2592000
|
||||
zoo24.de: max-age too low: 2592000
|
||||
|
@ -8,7 +8,7 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1408184023977000);
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1408788358092000);
|
||||
|
||||
class nsSTSPreload
|
||||
{
|
||||
@ -22,6 +22,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "aclu.org", false },
|
||||
{ "activiti.alfresco.com", false },
|
||||
{ "admin.google.com", true },
|
||||
{ "adsfund.org", true },
|
||||
{ "aladdinschools.appspot.com", false },
|
||||
{ "alpha.irccloud.com", false },
|
||||
{ "api.intercom.io", false },
|
||||
@ -39,7 +40,6 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "blog.linode.com", false },
|
||||
{ "blog.lookout.com", false },
|
||||
{ "blog.torproject.org", false },
|
||||
{ "blueseed.co", false },
|
||||
{ "boxcryptor.com", true },
|
||||
{ "braintreepayments.com", false },
|
||||
{ "bugzilla.mozilla.org", true },
|
||||
@ -51,6 +51,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "chrome.google.com", true },
|
||||
{ "chromiumcodereview.appspot.com", false },
|
||||
{ "cloud.google.com", true },
|
||||
{ "cloudcert.org", true },
|
||||
{ "cloudns.com.au", true },
|
||||
{ "cloudsecurityalliance.org", true },
|
||||
{ "code.google.com", true },
|
||||
@ -160,6 +161,12 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "opsmate.com", false },
|
||||
{ "p.linode.com", false },
|
||||
{ "packagist.org", false },
|
||||
{ "passport.yandex.by", false },
|
||||
{ "passport.yandex.com", false },
|
||||
{ "passport.yandex.com.tr", false },
|
||||
{ "passport.yandex.kz", false },
|
||||
{ "passport.yandex.ru", false },
|
||||
{ "passport.yandex.ua", false },
|
||||
{ "passwd.io", true },
|
||||
{ "passwordbox.com", false },
|
||||
{ "paste.linode.com", false },
|
||||
@ -174,6 +181,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "plus.sandbox.google.com", false },
|
||||
{ "profiles.google.com", true },
|
||||
{ "publications.qld.gov.au", false },
|
||||
{ "pult.co", true },
|
||||
{ "pypi.python.org", true },
|
||||
{ "python.org", false },
|
||||
{ "riseup.net", true },
|
||||
@ -184,6 +192,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "script.google.com", true },
|
||||
{ "security.google.com", true },
|
||||
{ "securityheaders.com", true },
|
||||
{ "seifried.org", true },
|
||||
{ "semenkovich.com", true },
|
||||
{ "shodan.io", true },
|
||||
{ "silentcircle.com", false },
|
||||
@ -207,6 +216,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "translate.googleapis.com", true },
|
||||
{ "twitter.com", false },
|
||||
{ "ubertt.org", true },
|
||||
{ "vmoagents.com", false },
|
||||
{ "wallet.google.com", true },
|
||||
{ "webmail.gigahost.dk", false },
|
||||
{ "webmail.onlime.ch", false },
|
||||
|
@ -51,8 +51,7 @@ user_pref("toolkit.telemetry.notifiedOptOut", 999);
|
||||
user_pref("font.size.inflation.emPerLine", 0);
|
||||
user_pref("font.size.inflation.minTwips", 0);
|
||||
|
||||
// AddonManager tests require that the experiments feature be enabled.
|
||||
user_pref("experiments.enabled", true);
|
||||
// AddonManager tests require that the experiments provider be present.
|
||||
user_pref("experiments.supported", true);
|
||||
user_pref("experiments.logging.level", "Trace");
|
||||
// Point the manifest at something local so we don't risk it hitting production
|
||||
|
@ -43,6 +43,7 @@
|
||||
#undef GetStartupInfo
|
||||
#endif
|
||||
|
||||
#include "mozilla/IOInterposer.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/StartupTimeline.h"
|
||||
|
||||
@ -696,7 +697,7 @@ nsAppStartup::Observe(nsISupports *aSubject,
|
||||
ExitLastWindowClosingSurvivalArea();
|
||||
} else if (!strcmp(aTopic, "sessionstore-windows-restored")) {
|
||||
StartupTimeline::Record(StartupTimeline::SESSION_RESTORED);
|
||||
Telemetry::LeavingStartupStage();
|
||||
IOInterposer::EnteringNextStage();
|
||||
#if defined(XP_WIN)
|
||||
if (mSessionWindowRestoredProbe) {
|
||||
mSessionWindowRestoredProbe->Trigger();
|
||||
@ -709,7 +710,7 @@ nsAppStartup::Observe(nsISupports *aSubject,
|
||||
} else if (!strcmp(aTopic, "sessionstore-init-started")) {
|
||||
StartupTimeline::Record(StartupTimeline::SESSION_RESTORE_INIT);
|
||||
} else if (!strcmp(aTopic, "xpcom-shutdown")) {
|
||||
Telemetry::EnteringShutdownStage();
|
||||
IOInterposer::EnteringNextStage();
|
||||
#if defined(XP_WIN)
|
||||
if (mXPCOMShutdownProbe) {
|
||||
mXPCOMShutdownProbe->Trigger();
|
||||
|
@ -326,24 +326,6 @@ public:
|
||||
*/
|
||||
void AddPath(const nsAString& aPath, const nsAString& aSubstName);
|
||||
|
||||
enum Stage
|
||||
{
|
||||
STAGE_STARTUP = 0,
|
||||
STAGE_NORMAL,
|
||||
STAGE_SHUTDOWN,
|
||||
NUM_STAGES
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets a new stage in the lifecycle of this process.
|
||||
* @param aNewStage One of the STAGE_* enum values.
|
||||
*/
|
||||
inline void SetStage(Stage aNewStage)
|
||||
{
|
||||
MOZ_ASSERT(aNewStage != NUM_STAGES);
|
||||
mCurStage = aNewStage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of hash table with file stats
|
||||
*/
|
||||
@ -364,6 +346,27 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
enum Stage
|
||||
{
|
||||
STAGE_STARTUP = 0,
|
||||
STAGE_NORMAL,
|
||||
STAGE_SHUTDOWN,
|
||||
NUM_STAGES
|
||||
};
|
||||
static inline Stage NextStage(Stage aStage)
|
||||
{
|
||||
switch (aStage) {
|
||||
case STAGE_STARTUP:
|
||||
return STAGE_NORMAL;
|
||||
case STAGE_NORMAL:
|
||||
return STAGE_SHUTDOWN;
|
||||
case STAGE_SHUTDOWN:
|
||||
return STAGE_SHUTDOWN;
|
||||
default:
|
||||
return NUM_STAGES;
|
||||
}
|
||||
}
|
||||
|
||||
struct FileStatsByStage
|
||||
{
|
||||
FileStats mStats[NUM_STAGES];
|
||||
@ -415,6 +418,12 @@ void TelemetryIOInterposeObserver::Observe(Observation& aOb)
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOb.ObservedOperation() == OpNextStage) {
|
||||
mCurStage = NextStage(mCurStage);
|
||||
MOZ_ASSERT(mCurStage < NUM_STAGES);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the filename
|
||||
const char16_t* filename = aOb.Filename();
|
||||
|
||||
@ -535,7 +544,8 @@ ClearIOReporting()
|
||||
if (!sTelemetryIOObserver) {
|
||||
return;
|
||||
}
|
||||
IOInterposer::Unregister(IOInterposeObserver::OpAll, sTelemetryIOObserver);
|
||||
IOInterposer::Unregister(IOInterposeObserver::OpAllWithStaging,
|
||||
sTelemetryIOObserver);
|
||||
sTelemetryIOObserver = nullptr;
|
||||
}
|
||||
|
||||
@ -2993,7 +3003,8 @@ InitIOReporting(nsIFile* aXreDir)
|
||||
}
|
||||
|
||||
sTelemetryIOObserver = new TelemetryIOInterposeObserver(aXreDir);
|
||||
IOInterposer::Register(IOInterposeObserver::OpAll, sTelemetryIOObserver);
|
||||
IOInterposer::Register(IOInterposeObserver::OpAllWithStaging,
|
||||
sTelemetryIOObserver);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3010,24 +3021,6 @@ SetProfileDir(nsIFile* aProfD)
|
||||
sTelemetryIOObserver->AddPath(profDirPath, NS_LITERAL_STRING("{profile}"));
|
||||
}
|
||||
|
||||
void
|
||||
LeavingStartupStage()
|
||||
{
|
||||
if (!sTelemetryIOObserver) {
|
||||
return;
|
||||
}
|
||||
sTelemetryIOObserver->SetStage(TelemetryIOInterposeObserver::STAGE_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
EnteringShutdownStage()
|
||||
{
|
||||
if (!sTelemetryIOObserver) {
|
||||
return;
|
||||
}
|
||||
sTelemetryIOObserver->SetStage(TelemetryIOInterposeObserver::STAGE_SHUTDOWN);
|
||||
}
|
||||
|
||||
void
|
||||
TimeHistogram::Add(PRIntervalTime aTime)
|
||||
{
|
||||
|
@ -295,7 +295,7 @@ exports.isSafeJSObject = function isSafeJSObject(aObj) {
|
||||
return true; // aObj is not a cross-compartment wrapper.
|
||||
}
|
||||
|
||||
let principal = Services.scriptSecurityManager.getObjectPrincipal(aObj);
|
||||
let principal = Cu.getObjectPrincipal(aObj);
|
||||
if (Services.scriptSecurityManager.isSystemPrincipal(principal)) {
|
||||
return true; // allow chrome objects
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ let gCategoryUtilities;
|
||||
let gExperiments;
|
||||
let gHttpServer;
|
||||
|
||||
let gSavedManifestURI;
|
||||
|
||||
function getExperimentAddons() {
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
@ -23,8 +25,16 @@ add_task(function* initializeState() {
|
||||
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("experiments.enabled");
|
||||
if (gHttpServer) {
|
||||
gHttpServer.stop(() => {});
|
||||
Services.prefs.clearUserPref("experiments.manifest.cert.checkAttributes");
|
||||
if (gSavedManifestURI !== undefined) {
|
||||
Services.prefs.setCharPref("experments.manifest.uri", gSavedManifestURI);
|
||||
}
|
||||
}
|
||||
if (gExperiments) {
|
||||
gExperiments._policy.ignoreHashes = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -167,24 +177,6 @@ add_task(function* testCleanup() {
|
||||
Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
|
||||
});
|
||||
|
||||
// We need to initialize the experiments service for the following tests.
|
||||
add_task(function* initializeExperiments() {
|
||||
if (!gExperiments) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to remove the cache file to help ensure consistent state.
|
||||
yield OS.File.remove(gExperiments._cacheFilePath);
|
||||
|
||||
info("Initializing experiments service.");
|
||||
yield gExperiments.init();
|
||||
info("Experiments service finished first run.");
|
||||
|
||||
// Check conditions, just to be sure.
|
||||
let experiments = yield gExperiments.getExperiments();
|
||||
Assert.equal(experiments.length, 0, "No experiments known to the service.");
|
||||
});
|
||||
|
||||
// The following tests should ideally live in browser/experiments/. However,
|
||||
// they rely on some of the helper functions from head.js, which can't easily
|
||||
// be consumed from other directories. So, they live here.
|
||||
@ -220,15 +212,24 @@ add_task(function* testActivateExperiment() {
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("experiments.manifest.cert.checkAttributes", false);
|
||||
gSavedManifestURI = Services.prefs.getCharPref("experiments.manifest.uri");
|
||||
Services.prefs.setCharPref("experiments.manifest.uri", root + "manifest");
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("experiments.manifest.cert.checkAttributes");
|
||||
Services.prefs.clearUserPref("experiments.manifest.uri");
|
||||
});
|
||||
|
||||
// We need to remove the cache file to help ensure consistent state.
|
||||
yield OS.File.remove(gExperiments._cacheFilePath);
|
||||
|
||||
Services.prefs.setBoolPref("experiments.enabled", true);
|
||||
|
||||
info("Initializing experiments service.");
|
||||
yield gExperiments.init();
|
||||
info("Experiments service finished first run.");
|
||||
|
||||
// Check conditions, just to be sure.
|
||||
let experiments = yield gExperiments.getExperiments();
|
||||
Assert.equal(experiments.length, 0, "No experiments known to the service.");
|
||||
|
||||
// This makes testing easier.
|
||||
gExperiments._policy.ignoreHashes = true;
|
||||
registerCleanupFunction(() => { gExperiments._policy.ignoreHashes = false; });
|
||||
|
||||
info("Manually updating experiments manifest.");
|
||||
yield gExperiments.updateManifest();
|
||||
@ -269,6 +270,10 @@ add_task(function testDeactivateExperiment() {
|
||||
|
||||
add_task(function* testCleanup() {
|
||||
if (gExperiments) {
|
||||
Services.prefs.clearUserPref("experiments.enabled");
|
||||
Services.prefs.clearUserPref("experiments.manifest.cert.checkAttributes");
|
||||
Services.prefs.setCharPref("experiments.manifest.uri", gSavedManifestURI);
|
||||
|
||||
// We perform the uninit/init cycle to purge any leftover state.
|
||||
yield OS.File.remove(gExperiments._cacheFilePath);
|
||||
yield gExperiments.uninit();
|
||||
|
@ -14,30 +14,6 @@ void ProfilerIOInterposeObserver::Observe(Observation& aObservation)
|
||||
return;
|
||||
}
|
||||
|
||||
const char* str = nullptr;
|
||||
|
||||
switch (aObservation.ObservedOperation()) {
|
||||
case IOInterposeObserver::OpCreateOrOpen:
|
||||
str = "create/open";
|
||||
break;
|
||||
case IOInterposeObserver::OpRead:
|
||||
str = "read";
|
||||
break;
|
||||
case IOInterposeObserver::OpWrite:
|
||||
str = "write";
|
||||
break;
|
||||
case IOInterposeObserver::OpFSync:
|
||||
str = "fsync";
|
||||
break;
|
||||
case IOInterposeObserver::OpStat:
|
||||
str = "stat";
|
||||
break;
|
||||
case IOInterposeObserver::OpClose:
|
||||
str = "close";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
ProfilerBacktrace* stack = profiler_get_backtrace();
|
||||
|
||||
nsCString filename;
|
||||
@ -50,5 +26,5 @@ void ProfilerIOInterposeObserver::Observe(Observation& aObservation)
|
||||
aObservation.Start(),
|
||||
aObservation.End(),
|
||||
stack);
|
||||
PROFILER_MARKER_PAYLOAD(str, markerPayload);
|
||||
PROFILER_MARKER_PAYLOAD(aObservation.ObservedOperationString(), markerPayload);
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "IOInterposer.h"
|
||||
|
||||
#include "IOInterposerPrivate.h"
|
||||
#include "MainThreadIOLogger.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
@ -47,7 +49,7 @@ void VectorRemove(std::vector<T>& vector, const T& element)
|
||||
}
|
||||
|
||||
/** Lists of Observers */
|
||||
struct ObserverLists : public AtomicRefCounted<ObserverLists>
|
||||
struct ObserverLists : public mozilla::AtomicRefCounted<ObserverLists>
|
||||
{
|
||||
ObserverLists()
|
||||
{
|
||||
@ -60,9 +62,10 @@ struct ObserverLists : public AtomicRefCounted<ObserverLists>
|
||||
, mFSyncObservers(aOther.mFSyncObservers)
|
||||
, mStatObservers(aOther.mStatObservers)
|
||||
, mCloseObservers(aOther.mCloseObservers)
|
||||
, mStageObservers(aOther.mStageObservers)
|
||||
{
|
||||
}
|
||||
// Lists of observers for read, write and fsync events respectively
|
||||
// Lists of observers for I/O events.
|
||||
// These are implemented as vectors since they are allowed to survive gecko,
|
||||
// without reporting leaks. This is necessary for the IOInterposer to be used
|
||||
// for late-write checks.
|
||||
@ -72,24 +75,7 @@ struct ObserverLists : public AtomicRefCounted<ObserverLists>
|
||||
std::vector<IOInterposeObserver*> mFSyncObservers;
|
||||
std::vector<IOInterposeObserver*> mStatObservers;
|
||||
std::vector<IOInterposeObserver*> mCloseObservers;
|
||||
};
|
||||
|
||||
/**
|
||||
* A quick and dirty RAII class to automatically lock a PRLock
|
||||
*/
|
||||
class AutoPRLock
|
||||
{
|
||||
PRLock* mLock;
|
||||
public:
|
||||
AutoPRLock(PRLock* aLock)
|
||||
: mLock(aLock)
|
||||
{
|
||||
PR_Lock(aLock);
|
||||
}
|
||||
~AutoPRLock()
|
||||
{
|
||||
PR_Unlock(mLock);
|
||||
}
|
||||
std::vector<IOInterposeObserver*> mStageObservers;
|
||||
};
|
||||
|
||||
class PerThreadData
|
||||
@ -144,6 +130,11 @@ public:
|
||||
observers = &mObserverLists->mCloseObservers;
|
||||
}
|
||||
break;
|
||||
case IOInterposeObserver::OpNextStage:
|
||||
{
|
||||
observers = &mObserverLists->mStageObservers;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// Invalid IO operation, see documentation comment for
|
||||
@ -194,16 +185,13 @@ class MasterList
|
||||
{
|
||||
public:
|
||||
MasterList()
|
||||
: mLock(PR_NewLock())
|
||||
, mObservedOperations(IOInterposeObserver::OpNone)
|
||||
: mObservedOperations(IOInterposeObserver::OpNone)
|
||||
, mIsEnabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
~MasterList()
|
||||
{
|
||||
PR_DestroyLock(mLock);
|
||||
mLock = nullptr;
|
||||
}
|
||||
|
||||
inline void
|
||||
@ -215,7 +203,7 @@ public:
|
||||
void
|
||||
Register(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
|
||||
{
|
||||
AutoPRLock lock(mLock);
|
||||
IOInterposer::AutoLock lock(mLock);
|
||||
|
||||
ObserverLists* newLists = nullptr;
|
||||
if (mObserverLists) {
|
||||
@ -249,6 +237,10 @@ public:
|
||||
!VectorContains(newLists->mCloseObservers, aObserver)) {
|
||||
newLists->mCloseObservers.push_back(aObserver);
|
||||
}
|
||||
if (aOp & IOInterposeObserver::OpNextStage &&
|
||||
!VectorContains(newLists->mStageObservers, aObserver)) {
|
||||
newLists->mStageObservers.push_back(aObserver);
|
||||
}
|
||||
mObserverLists = newLists;
|
||||
mObservedOperations = (IOInterposeObserver::Operation)
|
||||
(mObservedOperations | aOp);
|
||||
@ -259,7 +251,7 @@ public:
|
||||
void
|
||||
Unregister(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
|
||||
{
|
||||
AutoPRLock lock(mLock);
|
||||
IOInterposer::AutoLock lock(mLock);
|
||||
|
||||
ObserverLists* newLists = nullptr;
|
||||
if (mObserverLists) {
|
||||
@ -311,6 +303,13 @@ public:
|
||||
(mObservedOperations & ~IOInterposeObserver::OpClose);
|
||||
}
|
||||
}
|
||||
if (aOp & IOInterposeObserver::OpNextStage) {
|
||||
VectorRemove(newLists->mStageObservers, aObserver);
|
||||
if (newLists->mStageObservers.empty()) {
|
||||
mObservedOperations = (IOInterposeObserver::Operation)
|
||||
(mObservedOperations & ~IOInterposeObserver::OpNextStage);
|
||||
}
|
||||
}
|
||||
mObserverLists = newLists;
|
||||
mCurrentGeneration++;
|
||||
}
|
||||
@ -323,7 +322,7 @@ public:
|
||||
}
|
||||
// If the generation counts don't match then we need to update the current
|
||||
// thread's observer list with the new master list.
|
||||
AutoPRLock lock(mLock);
|
||||
IOInterposer::AutoLock lock(mLock);
|
||||
aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
|
||||
}
|
||||
|
||||
@ -345,7 +344,7 @@ private:
|
||||
// unregister observers during shutdown an OffTheBooksMutex is not an option
|
||||
// either, as its base calls into sDeadlockDetector which may be nullptr
|
||||
// during shutdown.
|
||||
PRLock* mLock;
|
||||
IOInterposer::Mutex mLock;
|
||||
// Flags tracking which operations are being observed
|
||||
IOInterposeObserver::Operation mObservedOperations;
|
||||
// Used for quickly disabling everything by IOInterposer::Disable()
|
||||
@ -354,6 +353,18 @@ private:
|
||||
Atomic<uint32_t> mCurrentGeneration;
|
||||
};
|
||||
|
||||
// Special observation used by IOInterposer::EnteringNextStage()
|
||||
class NextStageObservation : public IOInterposeObserver::Observation
|
||||
{
|
||||
public:
|
||||
NextStageObservation()
|
||||
: IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
|
||||
"IOInterposer", false)
|
||||
{
|
||||
mStart = TimeStamp::Now();
|
||||
}
|
||||
};
|
||||
|
||||
// List of observers registered
|
||||
static StaticAutoPtr<MasterList> sMasterList;
|
||||
static ThreadLocal<PerThreadData*> sThreadLocalData;
|
||||
@ -384,6 +395,29 @@ IOInterposeObserver::Observation::Observation(Operation aOperation,
|
||||
{
|
||||
}
|
||||
|
||||
const char*
|
||||
IOInterposeObserver::Observation::ObservedOperationString() const
|
||||
{
|
||||
switch(mOperation) {
|
||||
case OpCreateOrOpen:
|
||||
return "create/open";
|
||||
case OpRead:
|
||||
return "read";
|
||||
case OpWrite:
|
||||
return "write";
|
||||
case OpFSync:
|
||||
return "fsync";
|
||||
case OpStat:
|
||||
return "stat";
|
||||
case OpClose:
|
||||
return "close";
|
||||
case OpNextStage:
|
||||
return "NextStage";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IOInterposeObserver::Observation::Report()
|
||||
{
|
||||
@ -393,7 +427,7 @@ IOInterposeObserver::Observation::Report()
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
bool
|
||||
IOInterposer::Init()
|
||||
{
|
||||
// Don't initialize twice...
|
||||
@ -412,6 +446,8 @@ IOInterposer::Init()
|
||||
RegisterCurrentThread(isMainThread);
|
||||
sMasterList = new MasterList();
|
||||
|
||||
MainThreadIOLogger::Init();
|
||||
|
||||
// Now we initialize the various interposers depending on platform
|
||||
InitPoisonIOInterposer();
|
||||
// We don't hook NSPR on Windows because PoisonIOInterposer captures a
|
||||
@ -422,7 +458,7 @@ IOInterposer::Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
bool
|
||||
IOInterposeObserver::IsMainThread()
|
||||
{
|
||||
if (!sThreadLocalData.initialized()) {
|
||||
@ -435,13 +471,13 @@ IOInterposeObserver::IsMainThread()
|
||||
return ptd->IsMainThread();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::Clear()
|
||||
{
|
||||
sMasterList = nullptr;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::Disable()
|
||||
{
|
||||
if (!sMasterList) {
|
||||
@ -450,7 +486,7 @@ IOInterposer::Disable()
|
||||
sMasterList->Disable();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
|
||||
{
|
||||
MOZ_ASSERT(sMasterList);
|
||||
@ -475,13 +511,13 @@ IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
|
||||
ptd->CallObservers(aObservation);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
bool
|
||||
IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
|
||||
{
|
||||
return sMasterList && sMasterList->IsObservedOperation(aOp);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::Register(IOInterposeObserver::Operation aOp,
|
||||
IOInterposeObserver* aObserver)
|
||||
{
|
||||
@ -493,7 +529,7 @@ IOInterposer::Register(IOInterposeObserver::Operation aOp,
|
||||
sMasterList->Register(aOp, aObserver);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
|
||||
IOInterposeObserver* aObserver)
|
||||
{
|
||||
@ -504,7 +540,7 @@ IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
|
||||
sMasterList->Unregister(aOp, aObserver);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::RegisterCurrentThread(bool aIsMainThread)
|
||||
{
|
||||
if (!sThreadLocalData.initialized()) {
|
||||
@ -515,7 +551,7 @@ IOInterposer::RegisterCurrentThread(bool aIsMainThread)
|
||||
sThreadLocalData.set(curThreadData);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
void
|
||||
IOInterposer::UnregisterCurrentThread()
|
||||
{
|
||||
if (!sThreadLocalData.initialized()) {
|
||||
@ -527,3 +563,13 @@ IOInterposer::UnregisterCurrentThread()
|
||||
delete curThreadData;
|
||||
}
|
||||
|
||||
void
|
||||
IOInterposer::EnteringNextStage()
|
||||
{
|
||||
if (!sMasterList) {
|
||||
return;
|
||||
}
|
||||
NextStageObservation observation;
|
||||
Report(observation);
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,10 @@ public:
|
||||
OpFSync = (1 << 3),
|
||||
OpStat = (1 << 4),
|
||||
OpClose = (1 << 5),
|
||||
OpNextStage = (1 << 6), // Meta - used when leaving startup, entering shutdown
|
||||
OpWriteFSync = (OpWrite | OpFSync),
|
||||
OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose)
|
||||
OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose),
|
||||
OpAllWithStaging = (OpAll | OpNextStage)
|
||||
};
|
||||
|
||||
/** A representation of an I/O observation */
|
||||
@ -56,14 +58,19 @@ public:
|
||||
const TimeStamp& aEnd, const char* aReference);
|
||||
|
||||
/**
|
||||
* Operation observed, this is either OpRead, OpWrite or OpFSync,
|
||||
* combinations of these flags are only used when registering observers.
|
||||
* Operation observed, this is one of the individual Operation values.
|
||||
* Combinations of these flags are only used when registering observers.
|
||||
*/
|
||||
Operation ObservedOperation() const
|
||||
{
|
||||
return mOperation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the observed operation as a human-readable string.
|
||||
*/
|
||||
const char* ObservedOperationString() const;
|
||||
|
||||
/** Time at which the I/O operation was started */
|
||||
TimeStamp Start() const
|
||||
{
|
||||
@ -147,35 +154,24 @@ protected:
|
||||
};
|
||||
|
||||
/**
|
||||
* Class offering the public static IOInterposer API.
|
||||
*
|
||||
* This class is responsible for ensuring that events are routed to the
|
||||
* appropriate observers. Methods Init() and Clear() should only be called from
|
||||
* the main-thread at startup and shutdown, respectively.
|
||||
*
|
||||
* Remark: Instances of this class will never be created, you should consider it
|
||||
* to be a namespace containing static functions. The class is created to
|
||||
* facilitate to a private static instance variable sObservedOperations.
|
||||
* As we want to access this from an inline static methods, we have to do this
|
||||
* trick.
|
||||
* These functions are responsible for ensuring that events are routed to the
|
||||
* appropriate observers.
|
||||
*/
|
||||
class IOInterposer MOZ_FINAL
|
||||
namespace IOInterposer
|
||||
{
|
||||
// No instance of class should be created, they'd be empty anyway.
|
||||
IOInterposer();
|
||||
public:
|
||||
|
||||
/**
|
||||
* This function must be called from the main-thread when no other threads are
|
||||
* running before any of the other methods on this class may be used.
|
||||
*
|
||||
* IO reports can however, safely assume that IsObservedOperation() will
|
||||
* return false, until the IOInterposer is initialized.
|
||||
* return false until the IOInterposer is initialized.
|
||||
*
|
||||
* Remark, it's safe to call this method multiple times, so just call it when
|
||||
* you to utilize IO interposing.
|
||||
*
|
||||
* Using the IOInterposerInit class is preferred to calling this directly.
|
||||
*/
|
||||
static bool Init();
|
||||
bool Init();
|
||||
|
||||
/**
|
||||
* This function must be called from the main thread, and furthermore
|
||||
@ -185,16 +181,16 @@ public:
|
||||
* Callers should take care that no other consumers are subscribed to events,
|
||||
* as these events will stop when this function is called.
|
||||
*
|
||||
* In practice, we don't use this method, as the IOInterposer is used for
|
||||
* In practice, we don't use this method as the IOInterposer is used for
|
||||
* late-write checks.
|
||||
*/
|
||||
static void Clear();
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* This function immediately disables IOInterposer functionality in a fast,
|
||||
* thread-safe manner. Primarily for use by the crash reporter.
|
||||
*/
|
||||
static void Disable();
|
||||
void Disable();
|
||||
|
||||
/**
|
||||
* Report IO to registered observers.
|
||||
@ -216,14 +212,14 @@ public:
|
||||
* Remark: Init() must be called before any IO is reported. But
|
||||
* IsObservedOperation() will return false until Init() is called.
|
||||
*/
|
||||
static void Report(IOInterposeObserver::Observation& aObservation);
|
||||
void Report(IOInterposeObserver::Observation& aObservation);
|
||||
|
||||
/**
|
||||
* Return whether or not an operation is observed. Reporters should not
|
||||
* report operations that are not being observed by anybody. This mechanism
|
||||
* allows us to avoid reporting I/O when no observers are registered.
|
||||
*/
|
||||
static bool IsObservedOperation(IOInterposeObserver::Operation aOp);
|
||||
bool IsObservedOperation(IOInterposeObserver::Operation aOp);
|
||||
|
||||
/**
|
||||
* Register IOInterposeObserver, the observer object will receive all
|
||||
@ -231,7 +227,7 @@ public:
|
||||
*
|
||||
* Remark: Init() must be called before observers are registered.
|
||||
*/
|
||||
static void Register(IOInterposeObserver::Operation aOp,
|
||||
void Register(IOInterposeObserver::Operation aOp,
|
||||
IOInterposeObserver* aObserver);
|
||||
|
||||
/**
|
||||
@ -242,7 +238,7 @@ public:
|
||||
*
|
||||
* Remark: Init() must be called before observers are unregistered.
|
||||
*/
|
||||
static void Unregister(IOInterposeObserver::Operation aOp,
|
||||
void Unregister(IOInterposeObserver::Operation aOp,
|
||||
IOInterposeObserver* aObserver);
|
||||
|
||||
/**
|
||||
@ -256,7 +252,7 @@ public:
|
||||
* @param aIsMainThread true if IOInterposer should treat the current thread
|
||||
* as the main thread.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
RegisterCurrentThread(bool aIsMainThread = false);
|
||||
|
||||
/**
|
||||
@ -264,9 +260,16 @@ public:
|
||||
* to call when a thread is shutting down because it cleans up data that
|
||||
* is stored in a TLS slot.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
UnregisterCurrentThread();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called to inform observers that the process has transitioned out of the
|
||||
* startup stage or into the shutdown stage. Main thread only.
|
||||
*/
|
||||
void
|
||||
EnteringNextStage();
|
||||
} // namespace IOInterposer
|
||||
|
||||
class IOInterposerInit
|
||||
{
|
||||
|
167
xpcom/build/IOInterposerPrivate.h
Normal file
167
xpcom/build/IOInterposerPrivate.h
Normal file
@ -0,0 +1,167 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef xpcom_build_IOInterposerPrivate_h
|
||||
#define xpcom_build_IOInterposerPrivate_h
|
||||
|
||||
/* This header file contains declarations for helper classes that are
|
||||
to be used exclusively by IOInterposer and its observers. This header
|
||||
file is not to be used by anything else and MUST NOT be exported! */
|
||||
|
||||
#include <prcvar.h>
|
||||
#include <prlock.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace IOInterposer {
|
||||
|
||||
/**
|
||||
* The following classes are simple wrappers for PRLock and PRCondVar.
|
||||
* IOInterposer and friends use these instead of Mozilla::Mutex et al because
|
||||
* of the fact that IOInterposer is permitted to run until the process
|
||||
* terminates; we can't use anything that plugs into leak checkers or deadlock
|
||||
* detectors because IOInterposer will outlive those and generate false
|
||||
* positives.
|
||||
*/
|
||||
|
||||
class Monitor
|
||||
{
|
||||
public:
|
||||
Monitor()
|
||||
: mLock(PR_NewLock())
|
||||
, mCondVar(PR_NewCondVar(mLock))
|
||||
{
|
||||
}
|
||||
|
||||
~Monitor()
|
||||
{
|
||||
PR_DestroyCondVar(mCondVar);
|
||||
mCondVar = nullptr;
|
||||
PR_DestroyLock(mLock);
|
||||
mLock = nullptr;
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
PR_Lock(mLock);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
PR_Unlock(mLock);
|
||||
}
|
||||
|
||||
bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
|
||||
{
|
||||
return PR_WaitCondVar(mCondVar, aTimeout) == PR_SUCCESS;
|
||||
}
|
||||
|
||||
bool Notify()
|
||||
{
|
||||
return PR_NotifyCondVar(mCondVar) == PR_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
PRLock* mLock;
|
||||
PRCondVar* mCondVar;
|
||||
};
|
||||
|
||||
class MonitorAutoLock
|
||||
{
|
||||
public:
|
||||
MonitorAutoLock(Monitor &aMonitor)
|
||||
: mMonitor(aMonitor)
|
||||
{
|
||||
mMonitor.Lock();
|
||||
}
|
||||
|
||||
~MonitorAutoLock()
|
||||
{
|
||||
mMonitor.Unlock();
|
||||
}
|
||||
|
||||
bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
|
||||
{
|
||||
return mMonitor.Wait(aTimeout);
|
||||
}
|
||||
|
||||
bool Notify()
|
||||
{
|
||||
return mMonitor.Notify();
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor& mMonitor;
|
||||
};
|
||||
|
||||
class MonitorAutoUnlock
|
||||
{
|
||||
public:
|
||||
MonitorAutoUnlock(Monitor &aMonitor)
|
||||
: mMonitor(aMonitor)
|
||||
{
|
||||
mMonitor.Unlock();
|
||||
}
|
||||
|
||||
~MonitorAutoUnlock()
|
||||
{
|
||||
mMonitor.Lock();
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor& mMonitor;
|
||||
};
|
||||
|
||||
class Mutex
|
||||
{
|
||||
public:
|
||||
Mutex()
|
||||
: mPRLock(PR_NewLock())
|
||||
{
|
||||
}
|
||||
|
||||
~Mutex()
|
||||
{
|
||||
PR_DestroyLock(mPRLock);
|
||||
mPRLock = nullptr;
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
PR_Lock(mPRLock);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
PR_Unlock(mPRLock);
|
||||
}
|
||||
|
||||
private:
|
||||
PRLock* mPRLock;
|
||||
};
|
||||
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
AutoLock(Mutex& aLock)
|
||||
: mLock(aLock)
|
||||
{
|
||||
mLock.Lock();
|
||||
}
|
||||
|
||||
~AutoLock()
|
||||
{
|
||||
mLock.Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex& mLock;
|
||||
};
|
||||
|
||||
} // namespace IOInterposer
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // xpcom_build_IOInterposerPrivate_h
|
||||
|
221
xpcom/build/MainThreadIOLogger.cpp
Normal file
221
xpcom/build/MainThreadIOLogger.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MainThreadIOLogger.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "IOInterposerPrivate.h"
|
||||
#include "mozilla/IOInterposer.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
/**
|
||||
* This code uses NSPR stuff and STL containers because it must be detached
|
||||
* from leak checking code; this observer runs until the process terminates.
|
||||
*/
|
||||
|
||||
#include <prenv.h>
|
||||
#include <prprf.h>
|
||||
#include <prthread.h>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
struct ObservationWithStack
|
||||
{
|
||||
ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs,
|
||||
ProfilerBacktrace *aStack)
|
||||
: mObservation(aObs)
|
||||
, mStack(aStack)
|
||||
{
|
||||
const char16_t* filename = aObs.Filename();
|
||||
if (filename) {
|
||||
mFilename = filename;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::IOInterposeObserver::Observation mObservation;
|
||||
ProfilerBacktrace* mStack;
|
||||
nsString mFilename;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MainThreadIOLoggerImpl MOZ_FINAL : public IOInterposeObserver
|
||||
{
|
||||
public:
|
||||
MainThreadIOLoggerImpl();
|
||||
~MainThreadIOLoggerImpl();
|
||||
|
||||
bool Init();
|
||||
|
||||
void Observe(Observation& aObservation);
|
||||
|
||||
private:
|
||||
static void sIOThreadFunc(void* aArg);
|
||||
void IOThreadFunc();
|
||||
|
||||
TimeStamp mLogStartTime;
|
||||
const char* mFileName;
|
||||
PRThread* mIOThread;
|
||||
IOInterposer::Monitor mMonitor;
|
||||
bool mShutdownRequired;
|
||||
std::vector<ObservationWithStack> mObservations;
|
||||
};
|
||||
|
||||
static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl;
|
||||
|
||||
MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
|
||||
: mFileName(nullptr)
|
||||
, mIOThread(nullptr)
|
||||
, mShutdownRequired(false)
|
||||
{
|
||||
}
|
||||
|
||||
MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl()
|
||||
{
|
||||
if (!mIOThread) {
|
||||
return;
|
||||
}
|
||||
{ // Scope for lock
|
||||
IOInterposer::MonitorAutoLock lock(mMonitor);
|
||||
mShutdownRequired = true;
|
||||
lock.Notify();
|
||||
}
|
||||
PR_JoinThread(mIOThread);
|
||||
mIOThread = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
MainThreadIOLoggerImpl::Init()
|
||||
{
|
||||
if (mFileName) {
|
||||
// Already initialized
|
||||
return true;
|
||||
}
|
||||
mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
|
||||
if (!mFileName) {
|
||||
// Can't start
|
||||
return false;
|
||||
}
|
||||
mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this,
|
||||
PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD, 0);
|
||||
if (!mIOThread) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
|
||||
{
|
||||
PR_SetCurrentThreadName("MainThreadIOLogger");
|
||||
MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
|
||||
obj->IOThreadFunc();
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadIOLoggerImpl::IOThreadFunc()
|
||||
{
|
||||
PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
PR_IRUSR | PR_IWUSR | PR_IRGRP);
|
||||
if (!fd) {
|
||||
IOInterposer::MonitorAutoLock lock(mMonitor);
|
||||
mShutdownRequired = true;
|
||||
std::vector<ObservationWithStack>().swap(mObservations);
|
||||
return;
|
||||
}
|
||||
mLogStartTime = TimeStamp::Now();
|
||||
{ // Scope for lock
|
||||
IOInterposer::MonitorAutoLock lock(mMonitor);
|
||||
while (true) {
|
||||
while (!mShutdownRequired && mObservations.empty()) {
|
||||
lock.Wait();
|
||||
}
|
||||
if (mShutdownRequired) {
|
||||
break;
|
||||
}
|
||||
// Pull events off the shared array onto a local one
|
||||
std::vector<ObservationWithStack> observationsToWrite;
|
||||
observationsToWrite.swap(mObservations);
|
||||
|
||||
// Release the lock so that we're not holding anybody up during I/O
|
||||
IOInterposer::MonitorAutoUnlock unlock(mMonitor);
|
||||
|
||||
// Now write the events.
|
||||
for (std::vector<ObservationWithStack>::iterator
|
||||
i = observationsToWrite.begin(), e = observationsToWrite.end();
|
||||
i != e; ++i) {
|
||||
if (i->mObservation.ObservedOperation() == OpNextStage) {
|
||||
PR_fprintf(fd, "%f,NEXT-STAGE\n",
|
||||
(TimeStamp::Now() - mLogStartTime).ToMilliseconds());
|
||||
continue;
|
||||
}
|
||||
double durationMs = i->mObservation.Duration().ToMilliseconds();
|
||||
nsAutoCString nativeFilename;
|
||||
nativeFilename.AssignLiteral("(not available)");
|
||||
if (!i->mFilename.IsEmpty()) {
|
||||
if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) {
|
||||
nativeFilename.AssignLiteral("(conversion failed)");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Format:
|
||||
* Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
|
||||
*/
|
||||
if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n",
|
||||
(i->mObservation.Start() - mLogStartTime).ToMilliseconds(),
|
||||
i->mObservation.ObservedOperationString(), durationMs,
|
||||
i->mObservation.Reference(), nativeFilename.get()) > 0) {
|
||||
ProfilerBacktrace* stack = i->mStack;
|
||||
if (stack) {
|
||||
// TODO: Write out the callstack
|
||||
// (This will be added in a later bug)
|
||||
profiler_free_backtrace(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PR_Close(fd);
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadIOLoggerImpl::Observe(Observation& aObservation)
|
||||
{
|
||||
if (!mFileName || !IsMainThread()) {
|
||||
return;
|
||||
}
|
||||
IOInterposer::MonitorAutoLock lock(mMonitor);
|
||||
if (mShutdownRequired) {
|
||||
// The writer thread isn't running. Don't enqueue any more data.
|
||||
return;
|
||||
}
|
||||
// Passing nullptr as aStack parameter for now
|
||||
mObservations.push_back(ObservationWithStack(aObservation, nullptr));
|
||||
lock.Notify();
|
||||
}
|
||||
|
||||
namespace MainThreadIOLogger {
|
||||
|
||||
bool
|
||||
Init()
|
||||
{
|
||||
sImpl = new MainThreadIOLoggerImpl();
|
||||
if (!sImpl->Init()) {
|
||||
return false;
|
||||
}
|
||||
IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MainThreadIOLogger
|
||||
|
||||
} // namespace mozilla
|
||||
|
19
xpcom/build/MainThreadIOLogger.h
Normal file
19
xpcom/build/MainThreadIOLogger.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_MainThreadIOLogger_h
|
||||
#define mozilla_MainThreadIOLogger_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace MainThreadIOLogger {
|
||||
|
||||
bool Init();
|
||||
|
||||
} // namespace MainThreadIOLogger
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_MainThreadIOLogger_h
|
||||
|
@ -50,6 +50,7 @@ UNIFIED_SOURCES += [
|
||||
'FrozenFunctions.cpp',
|
||||
'IOInterposer.cpp',
|
||||
'LateWriteChecks.cpp',
|
||||
'MainThreadIOLogger.cpp',
|
||||
'nsXPComInit.cpp',
|
||||
'nsXPCOMStrings.cpp',
|
||||
'Services.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user