Merge backout

This commit is contained in:
Mats Palmgren 2014-04-20 19:35:54 +00:00
commit ad5500adec
74 changed files with 1986 additions and 326 deletions

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "38d1561fe26c12f371c44a47c498722ce06518c2",
"revision": "27357877d17b1547c81a26b66e97a27ed7b954ca",
"repo_path": "/integration/gaia-central"
}

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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"/>

View File

@ -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);

View File

@ -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";

View File

@ -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 {

View File

@ -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>

View File

@ -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();

View File

@ -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;
},
/**

View File

@ -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.

View File

@ -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%);
}

View File

@ -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%);
}

View File

@ -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%);
}

View File

@ -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.

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -309,6 +309,7 @@ PluginHangUIChild::HangUIDlgProc(HWND aDlgHandle, UINT aMsgCode, WPARAM aWParam,
}
case WM_DESTROY: {
EnableWindow(mParentWindow, TRUE);
SetForegroundWindow(mParentWindow);
break;
}
default:

View File

@ -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]

View 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;
};

View 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;

View 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);
};

View File

@ -0,0 +1,4 @@
<html>
<body onload="window.parent.frameLoaded()">
<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>

View 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>

View File

@ -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']

View 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)

View 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>

View 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/.

View File

@ -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,

View File

@ -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;
}

View 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);
});

View File

@ -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]

View File

@ -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;

View File

@ -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",

View 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);

View File

@ -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) {

View File

@ -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());

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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)));
}
}

View File

@ -13,6 +13,7 @@
_(UnboxFloatingPoint) \
_(DivI) \
_(DivPowTwoI) \
_(DivOrModConstantI) \
_(ModI) \
_(ModPowTwoI) \
_(PowHalfD) \

View File

@ -14,6 +14,7 @@
_(BoxFloatingPoint) \
_(DivI) \
_(DivPowTwoI) \
_(DivOrModConstantI) \
_(ModI) \
_(ModPowTwoI) \
_(PowHalfD) \

View File

@ -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);

View File

@ -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);
};
/**

View File

@ -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;
}
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/

View File

@ -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');
}

View File

@ -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

View File

@ -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 },

View File

@ -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

View File

@ -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();

View File

@ -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)
{

View File

@ -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
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
{

View 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

View 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

View 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

View File

@ -50,6 +50,7 @@ UNIFIED_SOURCES += [
'FrozenFunctions.cpp',
'IOInterposer.cpp',
'LateWriteChecks.cpp',
'MainThreadIOLogger.cpp',
'nsXPComInit.cpp',
'nsXPCOMStrings.cpp',
'Services.cpp',