Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2013-06-11 16:24:51 +01:00
commit 64b2bc1c50
107 changed files with 1533 additions and 1836 deletions

View File

@ -37,6 +37,7 @@
<stringbundleset id="pageinfobundleset">
<stringbundle id="pageinfobundle" src="chrome://browser/locale/pageInfo.properties"/>
<stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/>
<stringbundle id="browserBundle" src="chrome://browser/locale/browser.properties"/>
</stringbundleset>
<commandset id="pageInfoCommandSet">
@ -396,6 +397,7 @@
<label class="permPluginTemplateLabel"/>
<spacer flex="1"/>
<radiogroup class="permPluginTemplateRadioGroup" orient="horizontal" command="cmd_pluginsToggle">
<radio class="permPluginTemplateRadioDefault" label="&permUseDefault;"/>
<radio class="permPluginTemplateRadioAsk" label="&permAskAlways;"/>
<radio class="permPluginTemplateRadioAllow" label="&permAllow;"/>
<radio class="permPluginTemplateRadioBlock" label="&permBlock;"/>

View File

@ -188,14 +188,15 @@ function onRadioClick(aPartId)
var radioGroup = document.getElementById(aPartId + "RadioGroup");
var id = radioGroup.selectedItem.id;
var permission = id.split('#')[1];
permissionManager.add(gPermURI, aPartId, permission);
if (permission == UNKNOWN) {
permissionManager.remove(gPermURI, aPartId);
} else {
permissionManager.add(gPermURI, aPartId, permission);
}
if (aPartId == "indexedDB" &&
(permission == ALLOW || permission == BLOCK)) {
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
}
if (aPartId == "fullscreen" && permission == UNKNOWN) {
permissionManager.remove(gPermURI.host, "fullscreen");
}
}
function setRadioState(aPartId, aValue)
@ -268,21 +269,22 @@ function makeNicePluginName(aName) {
}
function fillInPluginPermissionTemplate(aPluginName, aPermissionString) {
let permPluginTemplate = document.getElementById("permPluginTemplate");
let permPluginTemplate = document.getElementById("permPluginTemplate").cloneNode(true);
permPluginTemplate.setAttribute("permString", aPermissionString);
let attrs = [
[ ".permPluginTemplateLabel", "value", aPluginName ],
[ ".permPluginTemplateRadioGroup", "id", aPermissionString + "RadioGroup" ],
[ ".permPluginTemplateRadioAsk", "id", aPermissionString + "#0" ],
[ ".permPluginTemplateRadioDefault", "id", aPermissionString + "#0" ],
[ ".permPluginTemplateRadioAsk", "id", aPermissionString + "#3" ],
[ ".permPluginTemplateRadioAllow", "id", aPermissionString + "#1" ],
[ ".permPluginTemplateRadioBlock", "id", aPermissionString + "#2" ]
];
for (let attr of attrs) {
document.querySelector(attr[0]).setAttribute(attr[1], attr[2]);
permPluginTemplate.querySelector(attr[0]).setAttribute(attr[1], attr[2]);
}
return permPluginTemplate.cloneNode(true);
return permPluginTemplate;
}
function clearPluginPermissionTemplate() {
@ -297,30 +299,36 @@ function clearPluginPermissionTemplate() {
}
function initPluginsRow() {
var vulnerableLabel = document.getElementById("browserBundle").getString("vulnerableNoUpdatePluginWarning");
let pluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
let tags = pluginHost.getPluginTags().filter(function(aTag) {
let mimeTypes = aTag.getMimeTypes();
if (mimeTypes.length < 1)
return false;
let mimeType = mimeTypes[0];
return (!aTag.disabled && pluginHost.isPluginClickToPlayForType(mimeType));
});
tags.sort(function(tagA, tagB) {
let nameA = makeNicePluginName(tagA.name);
let nameB = makeNicePluginName(tagB.name);
return nameA < nameB ? -1 : (nameA == nameB ? 0 : 1);
});
let permissionMap = Map();
let permissionEntries = [];
for (let plugin of tags) {
let mimeType = plugin.getMimeTypes()[0];
let permString = pluginHost.getPermissionStringForType(mimeType);
let pluginName = makeNicePluginName(plugin.name)
let permEntry = fillInPluginPermissionTemplate(pluginName, permString);
permissionEntries.push(permEntry);
for (let plugin of pluginHost.getPluginTags()) {
if (plugin.disabled) {
continue;
}
for (let mimeType of plugin.getMimeTypes()) {
let permString = pluginHost.getPermissionStringForType(mimeType);
if (!permissionMap.has(permString)) {
var name = makeNicePluginName(plugin.name);
if (permString.startsWith("plugin-vulnerable:")) {
name += " \u2014 " + vulnerableLabel;
}
permissionMap.set(permString, name);
}
}
}
let entries = [{name: item[1], permission: item[0]} for (item of permissionMap)];
entries.sort(function(a, b) {
return a.name < b.name ? -1 : (a.name == b.name ? 0 : 1);
});
let permissionEntries = [
fillInPluginPermissionTemplate(p.name, p.permission) for (p of entries)
];
let permPluginsRow = document.getElementById("permPluginsRow");
clearPluginPermissionTemplate();
if (permissionEntries.length < 1) {

View File

@ -318,6 +318,7 @@ _BROWSER_FILES = \
blockPluginVulnerableUpdatable.xml \
blockPluginVulnerableNoUpdate.xml \
blockNoPlugins.xml \
blockPluginHard.xml \
browser_utilityOverlay.js \
browser_bug676619.js \
download_page.html \

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
<emItems>
</emItems>
<pluginItems>
<pluginItem blockID="p9999">
<match name="filename" exp="libnptest\.so|nptest\.dll|Test\.plugin" />
<versionRange severity="2"></versionRange>
</pluginItem>
</pluginItems>
</blocklist>

View File

@ -1,5 +1,5 @@
const gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
const EXPECTED_PLUGINSCRIPTED_EVENT_COUNT = 7;
const EXPECTED_PLUGINSCRIPTED_EVENT_COUNT = 6;
var gTestBrowser = null;
var gNextTestList = [];
@ -107,16 +107,12 @@ function testDenyPermissionPart1() {
}
function testDenyPermissionPart2() {
var condition = function() gPluginScriptedFired;
var condition = function() XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).gScriptingFinished;
waitForCondition(condition, testDenyPermissionPart3, "test deny permission: waited too long for PluginScripted event");
}
function testDenyPermissionPart3() {
var condition = function() gTestBrowser._pluginScriptedState == gPluginHandler.PLUGIN_SCRIPTED_STATE_DONE;
waitForCondition(condition, testDenyPermissionPart4, "test deny permission: waited too long for PluginScripted event handling");
}
function testDenyPermissionPart4() {
ok(!gPluginScriptedFired, "Should not fire plugin-scripted event for disabled plugins");
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "test deny permission: should not have a click-to-play notification");

View File

@ -51,52 +51,57 @@ function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref("plugins.click_to_play", true);
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_ENABLED;
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
gPermissionManager.remove("127.0.0.1:8888", gTestPermissionString);
gPermissionManager.remove("127.0.0.1:8888", gSecondTestPermissionString);
doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart1a);
}
// By default, everything should be click-to-play. So: no plugins should be
// activated, and the radio buttons in Page Info should be "Always Ask"
// The first test plugin is CtP and the second test plugin is enabled.
function testPart1a() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "part 1a: should have a click-to-play notification");
let test = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part 1a: Test plugin should not be activated");
let secondtest = gTestBrowser.contentDocument.getElementById("secondtestA");
let objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part 1a: Second Test plugin should not be activated");
ok(objLoadingContent.activated, "part 1a: Second Test plugin should be activated");
doOnOpenPageInfo(testPart1b);
}
function testPart1b() {
let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
let testRadioAsk = gPageInfo.document.getElementById(gTestPermissionString + "#0");
is(testRadioGroup.selectedItem, testRadioAsk, "part 1b: Test radio group should be set to 'Always Ask'");
let testRadioDefault = gPageInfo.document.getElementById(gTestPermissionString + "#0");
var qString = "#" + gTestPermissionString.replace(':', '\\:') + "\\#0";
is(testRadioGroup.selectedItem, testRadioDefault, "part 1b: Test radio group should be set to 'Default'");
let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
testRadioGroup.selectedItem = testRadioAllow;
testRadioAllow.doCommand();
let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
is(secondtestRadioGroup.selectedItem, secondtestRadioAsk, "part 1b: Second Test radio group should be set to 'Always Ask'");
let secondtestRadioDefault = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
is(secondtestRadioGroup.selectedItem, secondtestRadioDefault, "part 1b: Second Test radio group should be set to 'Default'");
let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#3");
secondtestRadioGroup.selectedItem = secondtestRadioAsk;
secondtestRadioAsk.doCommand();
doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart2);
}
// Now, the Test plugin should be allowed.
// Now, the Test plugin should be allowed, and the Test2 plugin should be CtP
function testPart2() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "part 2: should have a click-to-play notification");
let test = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "part 2: Test plugin should be activated");
let secondtest = gTestBrowser.contentDocument.getElementById("secondtestA");
let objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part 2: Second Test plugin should not be activated");
let test = gTestBrowser.contentDocument.getElementById("test").
QueryInterface(Ci.nsIObjectLoadingContent);
ok(test.activated, "part 2: Test plugin should be activated");
let secondtest = gTestBrowser.contentDocument.getElementById("secondtestA").
QueryInterface(Ci.nsIObjectLoadingContent);
ok(!secondtest.activated, "part 2: Second Test plugin should not be activated");
is(secondtest.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
"part 2: Second test plugin should be click-to-play.");
let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
let testRadioAllow = gPageInfo.document.getElementById(gTestPermissionString + "#1");
@ -106,7 +111,7 @@ function testPart2() {
testRadioBlock.doCommand();
let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#3");
is(secondtestRadioGroup.selectedItem, secondtestRadioAsk, "part 2: Second Test radio group should be set to 'Always Ask'");
let secondtestRadioBlock = gPageInfo.document.getElementById(gSecondTestPermissionString + "#2");
secondtestRadioGroup.selectedItem = secondtestRadioBlock;
@ -117,19 +122,18 @@ function testPart2() {
// Now, all the things should be blocked
function testPart3() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "part 3: should not have a click-to-play notification");
let test = gTestBrowser.contentDocument.getElementById("test").
QueryInterface(Ci.nsIObjectLoadingContent);
ok(!test.activated, "part 3: Test plugin should not be activated");
is(test.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
"part 3: Test plugin should be marked as PLUGIN_DISABLED");
let test = gTestBrowser.contentDocument.getElementById("test");
let objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part 3: Test plugin should not be activated");
let overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(test, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "part 3: Test plugin should not have visible overlay");
let secondtest = gTestBrowser.contentDocument.getElementById("secondtestA");
let objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "part 3: Second Test plugin should not be activated");
let overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtest, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "part 3: Second Test plugin should not have visible overlay");
let secondtest = gTestBrowser.contentDocument.getElementById("secondtestA").
QueryInterface(Ci.nsIObjectLoadingContent);
ok(!secondtest.activated, "part 3: Second Test plugin should not be activated");
is(secondtest.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
"part 3: Second test plugin should be marked as PLUGIN_DISABLED");
// reset permissions
gPermissionManager.remove("127.0.0.1:8888", gTestPermissionString);
@ -137,21 +141,21 @@ function testPart3() {
// check that changing the permissions affects the radio state in the
// open Page Info window
let testRadioGroup = gPageInfo.document.getElementById(gTestPermissionString + "RadioGroup");
let testRadioAsk = gPageInfo.document.getElementById(gTestPermissionString + "#0");
is(testRadioGroup.selectedItem, testRadioAsk, "part 3: Test radio group should be set to 'Ask'");
let testRadioDefault = gPageInfo.document.getElementById(gTestPermissionString + "#0");
is(testRadioGroup.selectedItem, testRadioDefault, "part 3: Test radio group should be set to 'Default'");
let secondtestRadioGroup = gPageInfo.document.getElementById(gSecondTestPermissionString + "RadioGroup");
let secondtestRadioAsk = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
is(secondtestRadioGroup.selectedItem, secondtestRadioAsk, "part 3: Second Test radio group should be set to 'Always Ask'");
let secondtestRadioDefault = gPageInfo.document.getElementById(gSecondTestPermissionString + "#0");
is(secondtestRadioGroup.selectedItem, secondtestRadioDefault, "part 3: Second Test radio group should be set to 'Default'");
doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart4a);
}
// Now test that the popup notification influences Page Info
// Now test that setting permission directly (as from the popup notification)
// immediately influences Page Info.
function testPart4a() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "part 4a: should have a notification");
// simulate "always allow"
notification.secondaryActions[0].callback();
// simulate "allow" from the doorhanger
gPermissionManager.add(gTestBrowser.currentURI, gTestPermissionString, Ci.nsIPermissionManager.ALLOW_ACTION);
gPermissionManager.add(gTestBrowser.currentURI, gSecondTestPermissionString, Ci.nsIPermissionManager.ALLOW_ACTION);
// check (again) that changing the permissions affects the radio state in the
// open Page Info window
@ -179,43 +183,5 @@ function testPart4b() {
Services.prefs.setBoolPref("plugins.click_to_play", false);
gPageInfo.close();
doOnPageLoad(gHttpTestRoot + "plugin_two_types.html", testPart5a);
}
// check that if there are no click-to-play plugins, the plugin row is hidden
function testPart5a() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "part 5a: should not have a click-to-play notification");
doOnOpenPageInfo(testPart5b);
}
function testPart5b() {
ok(gPageInfo.document.getElementById("permPluginsRow").hidden, "part 5b: plugin permission row should be hidden");
gPageInfo.close();
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableUpdatable.xml",
function() {
doOnPageLoad(gHttpTestRoot + "plugin_test.html", testPart6a);
});
}
// check that if plugins.click_to_play is false, but there is a
// click-to-play blocklisted plugin, we show the plugin row
function testPart6a() {
let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "part 6a: should have a click-to-play notification");
doOnOpenPageInfo(testPart6b);
}
function testPart6b() {
ok(!gPageInfo.document.getElementById("permPluginsRow").hidden, "part 6b: plugin permission row should not be hidden");
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml",
function() {
resetBlocklist();
gPageInfo.close();
finishTest();
});
finishTest();
}

View File

@ -58,12 +58,16 @@ TabOpenListener.prototype = {
function test() {
waitForExplicitFinish();
requestLongerTimeout(2);
registerCleanupFunction(function() {
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
Services.prefs.clearUserPref("plugins.click_to_play");
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_ENABLED;
});
Services.prefs.setBoolPref("plugins.click_to_play", false);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
var plugin = getTestPlugin();
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
@ -117,7 +121,6 @@ function test1() {
var plugin = getTestPlugin();
ok(plugin, "Should have a test plugin");
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
plugin.blocklisted = false;
prepareTest(test2, gTestRoot + "plugin_test.html");
}
@ -159,14 +162,19 @@ function test4(tab, win) {
}
function prepareTest5() {
info("prepareTest5");
var plugin = getTestPlugin();
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
plugin.blocklisted = true;
prepareTest(test5, gTestRoot + "plugin_test.html");
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
function() {
info("prepareTest5 callback");
prepareTest(test5, gTestRoot + "plugin_test.html");
});
}
// Tests a page with a blocked plugin in it.
function test5() {
info("test5");
var notificationBox = gBrowser.getNotificationBox(gTestBrowser);
ok(!PopupNotifications.getNotification("plugins-not-found", gTestBrowser), "Test 5, Should not have displayed the missing plugin notification");
ok(notificationBox.getNotificationWithValue("blocked-plugins"), "Test 5, Should have displayed the blocked plugin notification");
@ -204,11 +212,12 @@ function test7() {
Services.prefs.setBoolPref("plugins.click_to_play", true);
var plugin = getTestPlugin();
plugin.blocklisted = false;
plugin.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test8, gTestRoot + "plugin_test.html");
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", function() {
prepareTest(test8, gTestRoot + "plugin_test.html");
});
}
// Tests a page with a working plugin that is click-to-play
@ -466,8 +475,10 @@ function test13c() {
var plugin = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 13c, Plugin should not be activated");
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED, "Test 13c, Plugin should be disabled");
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 13c, Plugin should not have visible overlay");
ok(overlay.style.visibility != "visible", "Test 13c, Plugin should have visible overlay");
prepareTest(test13d, gHttpTestRoot + "plugin_two_types.html");
}
@ -482,14 +493,16 @@ function test13d() {
var test = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(test, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 13d, Test plugin should not have visible overlay");
ok(overlay.style.visibility != "hidden", "Test 13d, Test plugin should have visible overlay");
ok(!objLoadingContent.activated, "Test 13d, Test plugin should not be activated");
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED, "Test 13d, Test plugin should be disabled");
var secondtestA = gTestBrowser.contentDocument.getElementById("secondtestA");
var objLoadingContent = secondtestA.QueryInterface(Ci.nsIObjectLoadingContent);
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtestA, "class", "mainBox");
ok(overlay.style.visibility != "hidden", "Test 13d, Test plugin should have visible overlay");
ok(!objLoadingContent.activated, "Test 13d, Second Test plugin (A) should not be activated");
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 13d, Test plugin should be disabled");
var secondtestB = gTestBrowser.contentDocument.getElementById("secondtestB");
var objLoadingContent = secondtestB.QueryInterface(Ci.nsIObjectLoadingContent);
@ -533,7 +546,6 @@ function test14() {
Services.prefs.setBoolPref("plugins.click_to_play", true);
var plugin = getTestPlugin();
plugin.blocklisted = false;
plugin.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
prepareTest(test15, gTestRoot + "plugin_alternate_content.html");
@ -1156,8 +1168,9 @@ function test25c() {
ok(secondtest, "Test 25c, Found second test plugin in page");
var objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 25c, second test plugin should not be activated");
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED, "Test 25c, second test plugin should be disabled");
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtest, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 25c, second test plugin should not have visible overlay");
ok(overlay.style.visibility != "hidden", "Test 25c, second test plugin should have visible overlay");
Services.perms.remove("127.0.0.1:8888", gPluginHost.getPermissionStringForType("application/x-test"));
Services.perms.remove("127.0.0.1:8888", gPluginHost.getPermissionStringForType("application/x-second-test"));

View File

@ -163,6 +163,8 @@ function test3() {
registerPlayPreview('application/x-test', 'about:');
Services.prefs.setBoolPref("plugins.click_to_play", false);
var plugin = getTestPlugin();
plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
prepareTest(test4, gTestRoot + "plugin_test.html");
}

View File

@ -4,11 +4,14 @@
<body onload="scriptPlugin()">
<embed id="test" type="application/x-test"/>
<script>
var gScriptingFinished = false;
function scriptPlugin() {
try {
document.getElementById("test").getObjectValue();
}
catch (e) {}
catch (e) {
setTimeout(function() { gScriptingFinished = true; }, 0);
}
}
</script>
</body>

View File

@ -529,26 +529,17 @@ IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
return false;
}
nsresult
IsPluginEnabledForType(const nsCString& aMIMEType)
bool
PluginExistsForType(const char* aMIMEType)
{
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
if (!pluginHost) {
NS_NOTREACHED("No pluginhost");
return NS_ERROR_FAILURE;
return false;
}
nsresult rv = pluginHost->IsPluginEnabledForType(aMIMEType.get());
// Check to see if the plugin is disabled before deciding if it
// should be in the "click to play" state, since we only want to
// display "click to play" UI for enabled plugins.
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
return pluginHost->PluginExistsForType(aMIMEType);
}
///
@ -1297,9 +1288,8 @@ nsObjectLoadingContent::UpdateObjectParameters()
thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
if (!classIDAttr.IsEmpty()) {
// Our classid support is limited to 'java:' ids
rv = IsPluginEnabledForType(NS_LITERAL_CSTRING("application/x-java-vm"));
if (NS_SUCCEEDED(rv) &&
StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:"))) {
if (StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:")) &&
PluginExistsForType("application/x-java-vm")) {
newMime.Assign("application/x-java-vm");
isJava = true;
} else {
@ -1709,16 +1699,7 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
// type, but the parameters are invalid e.g. a embed tag with type "image/png"
// but no URI -- don't show a plugin error or unknown type error in that case.
if (mType == eType_Null && GetTypeOfContent(mContentType) == eType_Null) {
// See if a disabled or blocked plugin could've handled this
nsresult pluginsupport = IsPluginEnabledForType(mContentType);
if (pluginsupport == NS_ERROR_PLUGIN_DISABLED) {
fallbackType = eFallbackDisabled;
} else if (pluginsupport == NS_ERROR_PLUGIN_BLOCKLISTED) {
fallbackType = eFallbackBlocklisted;
} else {
// Completely unknown type
fallbackType = eFallbackUnsupported;
}
fallbackType = eFallbackUnsupported;
}
// Explicit user activation should reset if the object changes content types
@ -2269,7 +2250,8 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
return eType_Document;
}
if ((caps & eSupportPlugins) && NS_SUCCEEDED(IsPluginEnabledForType(aMIMEType))) {
if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) {
// ShouldPlay will handle checking for disabled plugins
return eType_Plugin;
}
@ -2729,9 +2711,7 @@ nsObjectLoadingContent::CancelPlayPreview()
bool
nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
{
// mActivated is true if we've been activated via PlayPlugin() (e.g. user has
// clicked through). Otherwise, only play if click-to-play is off or if page
// is whitelisted
nsresult rv;
nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
@ -2754,32 +2734,46 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
return true;
}
bool isCTP;
nsresult rv = pluginHost->IsPluginClickToPlayForType(mContentType, &isCTP);
if (NS_FAILED(rv)) {
return false;
}
// Order of checks:
// * Already activated? Then ok
// * Assume a default of click-to-play
// * If blocklisted, override the reason with the blocklist reason
// * If not blocklisted but playPreview, override the reason with the
// playPreview reason.
// * Check per-site permissions and follow those if specified.
// * Honor per-plugin disabled permission
// * Blocklisted plugins are forced to CtP
// * Check per-plugin permission and follow that.
if (!isCTP || mActivated) {
if (mActivated) {
return true;
}
// set the fallback reason
// Before we check permissions, get the blocklist state of this plugin to set
// the fallback reason correctly.
aReason = eFallbackClickToPlay;
// (if it's click-to-play, it might be because of the blocklist)
uint32_t state;
rv = pluginHost->GetBlocklistStateForType(mContentType.get(), &state);
NS_ENSURE_SUCCESS(rv, false);
if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
pluginHost->GetBlocklistStateForType(mContentType.get(), &blocklistState);
if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
aReason = eFallbackVulnerableUpdatable;
}
else if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
aReason = eFallbackVulnerableNoUpdate;
}
else if (blocklistState == nsIBlocklistService::STATE_BLOCKED) {
// no override possible
aReason = eFallbackBlocklisted;
return false;
}
// If plugin type is click-to-play and we have not been explicitly clicked.
// check if permissions lets this page bypass - (e.g. user selected 'Always
// play plugins on this page')
if (aReason == eFallbackClickToPlay && isPlayPreviewSpecified &&
!mPlayPreviewCanceled && !ignoreCTP) {
// play preview in click-to-play mode is shown instead of standard CTP UI
aReason = eFallbackPlayPreview;
}
// Check the permission manager for permission based on the principal of
// the toplevel content.
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
MOZ_ASSERT(thisContent);
@ -2800,7 +2794,6 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, false);
bool allowPerm = false;
// For now we always say that the system principal uses click-to-play since
// that maintains current behavior and we have tests that expect this.
// What we really should do is disable plugins entirely in pages that use
@ -2815,16 +2808,43 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
permissionString.Data(),
&permission);
NS_ENSURE_SUCCESS(rv, false);
allowPerm = permission == nsIPermissionManager::ALLOW_ACTION;
switch (permission) {
case nsIPermissionManager::ALLOW_ACTION:
return true;
case nsIPermissionManager::DENY_ACTION:
aReason = eFallbackDisabled;
return false;
case nsIPermissionManager::PROMPT_ACTION:
return false;
case nsIPermissionManager::UNKNOWN_ACTION:
break;
default:
MOZ_ASSERT(false);
return false;
}
}
if (aReason == eFallbackClickToPlay && isPlayPreviewSpecified &&
!mPlayPreviewCanceled && !ignoreCTP) {
// play preview in click-to-play mode is shown instead of standard CTP UI
aReason = eFallbackPlayPreview;
uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
pluginHost->GetStateForType(mContentType, &enabledState);
if (nsIPluginTag::STATE_DISABLED == enabledState) {
aReason = eFallbackDisabled;
return false;
}
return allowPerm;
// No site-specific permissions. Vulnerable plugins are automatically CtP
if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
return false;
}
switch (enabledState) {
case nsIPluginTag::STATE_ENABLED:
return true;
case nsIPluginTag::STATE_CLICKTOPLAY:
return false;
}
MOZ_NOT_REACHED("Unexpected enabledState");
return false;
}
nsIDocument*

View File

@ -17,6 +17,8 @@ MOCHITEST_FILES = \
MOCHITEST_CHROME_FILES = \
test_bug206691.xul \
test_bug391728.html \
blockPluginHard.xml \
blockNoPlugins.xml \
file_bug391728.html \
file_bug391728_2.html \
test_bug421622.xul \

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310001">
<emItems>
</emItems>
<pluginItems>
</pluginItems>
</blocklist>

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
<emItems>
</emItems>
<pluginItems>
<pluginItem blockID="p9999">
<match name="filename" exp="libnptest\.so|nptest\.dll|Test\.plugin" />
<versionRange severity="2"></versionRange>
</pluginItem>
</pluginItems>
</blocklist>

View File

@ -36,24 +36,20 @@ document.addEventListener("PluginBindingAttached", plugin_binding_attached, true
<div><object id="plugin3" style="width: 100px; height: 100px" type="application/x-test"></object></div>
<div><object id="plugin4" style="width: 100px; height: 100px" data="data:application/x-test,test"></object></div>
<!-- The mimetype of the actual data is supposed to be used in preference -->
<div><embed id="plugin5" style="width: 100px; height: 100px" type="application/x-unknown" src="data:application/x-test,test"></div>
<div><object id="plugin6" style="width: 100px; height: 100px" type="application/x-unknown" data="data:application/x-test,test"></object></div>
<!-- Params are not considered content -->
<div><object id="plugin7" style="width: 100px; height: 100px" type="application/x-test">
<div><object id="plugin5" style="width: 100px; height: 100px" type="application/x-test">
<param name="foo" value="bar">
</object></div>
<div><object id="plugin8" style="width: 100px; height: 100px" data="data:application/x-test,test">
<div><object id="plugin6" style="width: 100px; height: 100px" data="data:application/x-test,test">
<param name="foo" value="bar">
</object></div>
<!-- Nor is whitespace -->
<div><object id="plugin9" style="width: 100px; height: 100px" type="application/x-test">
<div><object id="plugin7" style="width: 100px; height: 100px" type="application/x-test">
</object></div>
<div><object id="plugin10" style="width: 100px; height: 100px" data="data:application/x-test,test">
<div><object id="plugin8" style="width: 100px; height: 100px" data="data:application/x-test,test">
</object></div>
@ -69,14 +65,14 @@ document.addEventListener("PluginBindingAttached", plugin_binding_attached, true
<!-- Even other plugins are considered content so no errors dispatched from these
objects, but the inner embeds do get processed -->
<div><object id="fallback3" style="width: 100px; height: 100px" type="application/x-test">
<embed id="plugin11" style="width: 100px; height: 100px" type="application/x-test">
<embed id="plugin9" style="width: 100px; height: 100px" type="application/x-test">
</object></div>
<div><object id="fallback4" style="width: 100px; height: 100px" data="data:application/x-test,test">
<embed id="plugin12" style="width: 100px; height: 100px" type="application/x-test">
<embed id="plugin10" style="width: 100px; height: 100px" type="application/x-test">
</object></div>
<!-- pluginurl was removed in bug 548133, and should not affect fallback -->
<div><object id="plugin13" style="width: 100px; height: 100px" data="data:application/x-test,test">
<div><object id="plugin11" style="width: 100px; height: 100px" data="data:application/x-test,test">
<param name="pluginurl">
</object></div>

View File

@ -36,24 +36,20 @@ document.addEventListener("PluginBindingAttached", plugin_binding_attached, true
<div><object id="plugin3" style="width: 100px; height: 100px" type="application/x-unknown"></object></div>
<div><object id="plugin4" style="width: 100px; height: 100px" data="data:application/x-unknown,test"></object></div>
<!-- The mimetype of the actual data is supposed to be used in preference -->
<div><embed id="plugin5" style="width: 100px; height: 100px" type="application/x-test" src="data:application/x-unknown,test"></div>
<div><object id="plugin6" style="width: 100px; height: 100px" type="application/x-test" data="data:application/x-unknown,test"></object></div>
<!-- Params are not considered content -->
<div><object id="plugin7" style="width: 100px; height: 100px" type="application/x-unknown">
<div><object id="plugin5" style="width: 100px; height: 100px" type="application/x-unknown">
<param name="foo" value="bar">
</object></div>
<div><object id="plugin8" style="width: 100px; height: 100px" data="data:application/x-unknown,test">
<div><object id="plugin6" style="width: 100px; height: 100px" data="data:application/x-unknown,test">
<param name="foo" value="bar">
</object></div>
<!-- Nor is whitespace -->
<div><object id="plugin9" style="width: 100px; height: 100px" type="application/x-unknown">
<div><object id="plugin7" style="width: 100px; height: 100px" type="application/x-unknown">
</object></div>
<div><object id="plugin10" style="width: 100px; height: 100px" data="data:application/x-unknown,test">
<div><object id="plugin8" style="width: 100px; height: 100px" data="data:application/x-unknown,test">
</object></div>
@ -69,14 +65,14 @@ document.addEventListener("PluginBindingAttached", plugin_binding_attached, true
<!-- Even other plugins are considered content so no errors dispatched from these
objects, but the inner embeds do get processed -->
<div><object id="fallback3" style="width: 100px; height: 100px" type="application/x-unknown">
<embed id="plugin11" style="width: 100px; height: 100px" type="application/x-unknown">
<embed id="plugin9" style="width: 100px; height: 100px" type="application/x-unknown">
</object></div>
<div><object id="fallback4" style="width: 100px; height: 100px" data="data:application/x-unknown,test">
<embed id="plugin12" style="width: 100px; height: 100px" type="application/x-unknown">
<embed id="plugin10" style="width: 100px; height: 100px" type="application/x-unknown">
</object></div>
<!-- pluginurl was removed in bug 548133, and should not affect fallback -->
<div><object id="plugin13" style="width: 100px; height: 100px" type="application/x-unknown">
<div><object id="plugin11" style="width: 100px; height: 100px" type="application/x-unknown">
<param name="pluginurl">
</object></div>

View File

@ -17,9 +17,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=391728
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
const gHttpTestRoot = location.toString().replace("chrome://mochitests/content/", "http://127.0.0.1:8888/").split(/\//).slice(0, -1).join('/') + '/';
Components.utils.import("resource://gre/modules/Services.jsm");
/** Test for Bug 391728 **/
// Plugins that should dispatch error events and have the pseudo classes set
const PLUGIN_COUNT = 13;
const PLUGIN_COUNT = 11;
// Plugins that should neither dispatch error events or have the pseudo classes set
const FALLBACK_COUNT = 5;
const OBJLC = Components.interfaces.nsIObjectLoadingContent;
@ -46,16 +50,39 @@ function plugin_binding_attached(event) {
}
function init_test() {
// Make sure the blocklist is off for the duration of this test
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.setBoolPref("extensions.blocklist.enabled", false);
if (!PluginUtils.withTestPlugin(start_test))
SimpleTest.finish();
}
function updateBlocklist(aCallback) {
var blocklistNotifier = Components.classes["@mozilla.org/extensions/blocklist;1"]
.getService(Components.interfaces.nsITimerCallback);
var observer = function() {
aCallback();
Services.obs.removeObserver(observer, "blocklist-updated");
};
Services.obs.addObserver(observer, "blocklist-updated", false);
blocklistNotifier.notify(null);
}
var _originalBlocklistURL = null;
function setAndUpdateBlocklist(aURL, aCallback) {
info("Setting blocklist to " + aURL);
if (!_originalBlocklistURL) {
_originalBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
}
Services.prefs.setCharPref("extensions.blocklist.url", aURL);
updateBlocklist(aCallback);
}
function resetBlocklist() {
info("resetting blocklist URL to " + _originalBlocklistURL);
Services.prefs.setCharPref("extensions.blocklist.url", _originalBlocklistURL);
}
function start_test(plugin) {
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
is(plugin.description, "Plug-in for testing purposes.\u2122 " +
"(\u0939\u093f\u0928\u094d\u0926\u0940 " +
"\u4e2d\u6587 " +
@ -71,11 +98,9 @@ function start_test(plugin) {
}
function finish_test(plugin) {
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
resetBlocklist();
plugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
plugin.blocklisted = false;
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.clearUserPref("extensions.blocklist.enabled");
SimpleTest.finish();
}
@ -139,8 +164,11 @@ function test_disabled(plugin) {
test_style("dotted");
ok(plugin.disabled, "Plugin lost its disabled status");
plugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
plugin.blocklisted = true;
load_frame(test_blocked, "file_bug391728");
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
function() {
load_frame(test_blocked, "file_bug391728");
});
}
function test_blocked(plugin) {
@ -159,7 +187,10 @@ function test_unknown(plugin) {
is(gDisabled.length, 0, "Should not have been any disabled plugins");
is(gBlocked.length, 0, "Should not have been any blocked plugins");
test_style("none");
finish_test(plugin);
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", function() {
ok(!plugin.blocklisted, "Plugin shouldn't remain blocklisted");
finish_test(plugin);
});
}
SimpleTest.waitForExplicitFinish();

View File

@ -97,6 +97,9 @@ interface nsIPluginHost : nsISupports
ACString getPermissionStringForType(in AUTF8String mimeType);
bool isPluginClickToPlayForType(in AUTF8String mimeType);
/**
* Get the nsIPluginTag state for this MIME type.
*/
unsigned long getStateForType(in AUTF8String mimeType);
};

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
[scriptable, uuid(00ea1f72-a57a-49bb-ac57-478902d9228b)]
[scriptable, uuid(0354d96d-3fd3-4365-a0a2-6c807ec1ce95)]
interface nsIPluginTag : nsISupports
{
// enabledState is stored as one of the following as an integer in prefs,
@ -19,7 +19,12 @@ interface nsIPluginTag : nsISupports
readonly attribute AUTF8String fullpath;
readonly attribute AUTF8String version;
readonly attribute AUTF8String name;
attribute boolean blocklisted;
/**
* true only if this plugin is "hardblocked" and cannot be enabled.
*/
readonly attribute boolean blocklisted;
readonly attribute boolean disabled;
readonly attribute boolean clicktoplay;
attribute unsigned long enabledState;

View File

@ -1790,13 +1790,6 @@ nsNPAPIPluginInstance::CheckJavaC2PJSObjectQuirk(uint16_t paramCount,
return;
}
bool isClickToPlay;
nsAutoCString mimeType(mMIMEType);
rv = pluginHost->IsPluginClickToPlayForType(mimeType, &isClickToPlay);
if (NS_FAILED(rv) || !isClickToPlay) {
return;
}
nsPluginTag* pluginTag = pluginHost->TagForPlugin(mPlugin);
if (!pluginTag ||
!pluginTag->mIsJavaPlugin) {
@ -1807,10 +1800,10 @@ nsNPAPIPluginInstance::CheckJavaC2PJSObjectQuirk(uint16_t paramCount,
bool haveCodeParam = false;
bool isCodeParamEmpty = true;
for (uint16_t i = 0; i < paramCount; ++i) {
if (PL_strcasecmp(paramNames[i], "code") == 0) {
for (uint16_t i = paramCount; i > 0; --i) {
if (PL_strcasecmp(paramNames[i - 1], "code") == 0) {
haveCodeParam = true;
if (strlen(paramValues[i]) > 0) {
if (strlen(paramValues[i - 1]) > 0) {
isCodeParamEmpty = false;
}
break;

View File

@ -1043,57 +1043,31 @@ nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
return rv;
}
nsresult
nsPluginHost::IsPluginEnabledForType(const char* aMimeType)
bool
nsPluginHost::PluginExistsForType(const char* aMimeType)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, true);
if (plugin)
return NS_OK;
// Pass false as the second arg so we can return NS_ERROR_PLUGIN_DISABLED
// for disabled plug-ins.
plugin = FindPluginForType(aMimeType, false);
if (!plugin)
return NS_ERROR_FAILURE;
if (!plugin->IsActive()) {
if (plugin->IsBlocklisted())
return NS_ERROR_PLUGIN_BLOCKLISTED;
else
return NS_ERROR_PLUGIN_DISABLED;
}
return NS_OK;
nsPluginTag *plugin = FindPluginForType(aMimeType, false);
return nullptr != plugin;
}
NS_IMETHODIMP
nsPluginHost::IsPluginClickToPlayForType(const nsACString &aMimeType, bool *aResult)
nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
{
nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
if (!plugin) {
return NS_ERROR_UNEXPECTED;
}
uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
NS_ENSURE_SUCCESS(rv, rv);
if ((mPluginsClickToPlay && plugin->IsClicktoplay()) ||
blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE ||
blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
*aResult = true;
}
else {
*aResult = false;
}
return NS_OK;
return plugin->GetEnabledState(aResult);
}
nsresult
nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, true);
if (!plugin) {
plugin = FindPluginForType(aMimeType, false);
}
if (plugin) {
nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
if (blocklist) {
@ -1115,6 +1089,9 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString
nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
NS_ENSURE_SUCCESS(rv, rv);
nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
if (!tag) {
tag = FindPluginForType(aMimeType.Data(), false);
}
if (!tag) {
return NS_ERROR_FAILURE;
}
@ -1967,16 +1944,10 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
EmptyString(), &state);
if (NS_SUCCEEDED(rv)) {
// If the blocklist says so, block the plugin.
// If the blocklist says it is risky and we have never seen this
// plugin before, then disable it.
// If the blocklist says this is an outdated plugin, warn about
// outdated plugins.
// If the blocklist says the plugin is one of the click-to-play
// states, set the click-to-play flag.
if (state == nsIBlocklistService::STATE_BLOCKED) {
pluginTag->SetBlocklisted(true);
}
if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
}

View File

@ -79,7 +79,8 @@ public:
nsresult SetUpPluginInstance(const char *aMimeType,
nsIURI *aURL,
nsPluginInstanceOwner *aOwner);
nsresult IsPluginEnabledForType(const char* aMimeType);
bool PluginExistsForType(const char* aMimeType);
nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
nsresult GetBlocklistStateForType(const char *aMimeType, uint32_t *state);

View File

@ -11,6 +11,7 @@
#include "nsServiceManagerUtils.h"
#include "nsPluginsDir.h"
#include "nsPluginHost.h"
#include "nsIBlocklistService.h"
#include "nsIUnicodeDecoder.h"
#include "nsIPlatformCharset.h"
#include "nsICharsetConverterManager.h"
@ -30,7 +31,6 @@ using mozilla::TimeStamp;
// no longer used 0x0002 // reuse only if regenerating pluginreg.dat
#define NS_PLUGIN_FLAG_FROMCACHE 0x0004 // this plugintag info was loaded from cache
// no longer used 0x0008 // reuse only if regenerating pluginreg.dat
#define NS_PLUGIN_FLAG_BLOCKLISTED 0x0010 // this is a blocklisted plugin
#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
inline char* new_str(const char* str)
@ -63,12 +63,6 @@ GetStatePrefNameForPlugin(nsPluginTag* aTag)
return MakePrefNameForPlugin("state", aTag);
}
static nsCString
GetBlocklistedPrefNameForPlugin(nsPluginTag* aTag)
{
return MakePrefNameForPlugin("blocklisted", aTag);
}
NS_IMPL_ISUPPORTS1(DOMMimeTypeImpl, nsIDOMMimeType)
/* nsPluginTag */
@ -332,7 +326,14 @@ nsPluginTag::GetDisabled(bool* aDisabled)
bool
nsPluginTag::IsBlocklisted()
{
return Preferences::GetBool(GetBlocklistedPrefNameForPlugin(this).get(), false);
nsCOMPtr<nsIBlocklistService> bls = do_GetService("@mozilla.org/extensions/blocklist;1");
if (!bls) {
return false;
}
uint32_t state = nsIBlocklistService::STATE_NOT_BLOCKED;
bls->GetPluginBlocklistState(this, EmptyString(), EmptyString(), &state);
return state == nsIBlocklistService::STATE_BLOCKED;
}
NS_IMETHODIMP
@ -342,26 +343,6 @@ nsPluginTag::GetBlocklisted(bool* aBlocklisted)
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::SetBlocklisted(bool blocklisted)
{
if (blocklisted == IsBlocklisted()) {
return NS_OK;
}
const nsCString pref = GetBlocklistedPrefNameForPlugin(this);
if (blocklisted) {
Preferences::SetBool(pref.get(), true);
} else {
Preferences::ClearUser(pref.get());
}
if (nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst()) {
host->UpdatePluginInfo(this);
}
return NS_OK;
}
bool
nsPluginTag::IsClicktoplay()
{
@ -556,8 +537,4 @@ void nsPluginTag::ImportFlagsToPrefs(uint32_t flags)
if (!(flags & NS_PLUGIN_FLAG_ENABLED)) {
SetPluginState(ePluginState_Disabled);
}
if (flags & NS_PLUGIN_FLAG_BLOCKLISTED) {
Preferences::SetBool(GetBlocklistedPrefNameForPlugin(this).get(), true);
}
}

View File

@ -11,7 +11,7 @@
Components.utils.import("resource://gre/modules/Services.jsm");
var pluginHost = Components.classes["@mozilla.org/plugin/host;1"]
.getService(Components.interfaces.nsIPluginHost);
ok(!pluginHost.isPluginClickToPlayForType("application/x-test"), "click-to-play should be off to begin with");
is(pluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_ENABLED, "click-to-play should be off to begin with");
Services.prefs.setBoolPref("plugins.click_to_play", true);
var pluginTags = pluginHost.getPluginTags();
@ -24,11 +24,11 @@
}
ok(testPlugin, "Should have Test Plug-in");
testPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY;
ok(pluginHost.isPluginClickToPlayForType("application/x-test"), "click-to-play should be on for Test Plug-in now");
ok(!pluginHost.isPluginClickToPlayForType("application/x-second-test"), "click-to-play should still be off for the Second Test Plug-in");
is(pluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY, "click-to-play should be on for Test Plug-in now");
is(pluginHost.getStateForType("application/x-second-test"), Components.interfaces.nsIPluginTag.STATE_ENABLED, "click-to-play should still be off for the Second Test Plug-in");
testPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
ok(!pluginHost.isPluginClickToPlayForType("application/x-test"), "click-to-play should be off for Test Plug-in now");
is(pluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_ENABLED, "click-to-play should be off for Test Plug-in now");
Services.prefs.clearUserPref("plugins.click_to_play");
</script>

View File

@ -55,7 +55,6 @@ function run_test() {
const pluginName = file.leafName.substring(0, file.leafName.length - suffix.length).toLowerCase();
const pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
const statePref = "plugin.state." + pluginName;
const blocklistedPref = "plugin.blocklisted." + pluginName;
// write plugin registry data
let registry = "";
@ -83,7 +82,6 @@ function run_test() {
// check that the expected plugin state was loaded correctly from the registry
do_check_true(plugin.disabled);
do_check_false(plugin.blocklisted);
do_check_false(plugin.clicktoplay);
// ... and imported into prefs, with 0 being the disabled state
do_check_eq(0, Services.prefs.getIntPref(statePref));

View File

@ -32,7 +32,6 @@ public:
FrameMetrics()
: mCompositionBounds(0, 0, 0, 0)
, mContentRect(0, 0, 0, 0)
, mDisplayPort(0, 0, 0, 0)
, mCriticalDisplayPort(0, 0, 0, 0)
, mViewport(0, 0, 0, 0)
@ -51,7 +50,6 @@ public:
bool operator==(const FrameMetrics& aOther) const
{
return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
mContentRect.IsEqualEdges(aOther.mContentRect) &&
mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
mViewport.IsEqualEdges(aOther.mViewport) &&
@ -122,17 +120,6 @@ public:
// metric as they do not have a displayport set. See bug 775452.
LayerIntRect mCompositionBounds;
// |mScrollableRect|, stored in layer pixels. DECPRECATED, DO NOT USE.
//
// This is valid on any layer where |mScrollableRect| is, though it may be
// more lazily maintained than |mScrollableRect|. That is, when
// |mScrollableRect| is updated, this may lag. For this reason, it's better to
// use |mScrollableRect| for any control logic.
//
// FIXME/bug 785929: Is this really necessary? Can it not be calculated from
// |mScrollableRect| whenever it's needed?
LayerIntRect mContentRect;
// ---------------------------------------------------------------------------
// The following metrics are all in CSS pixels. They are not in any uniform
// space, so each is explained separately.

View File

@ -411,15 +411,14 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const gfx3DMatr
metrics.mScrollOffset, layerPixelRatioX, layerPixelRatioY);
if (mIsFirstPaint) {
mContentRect = metrics.mContentRect;
mContentRect = metrics.mScrollableRect;
SetFirstPaintViewport(scrollOffsetLayerPixels,
layerPixelRatioX,
mContentRect,
metrics.mScrollableRect);
mContentRect);
mIsFirstPaint = false;
} else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) {
mContentRect = metrics.mContentRect;
SetPageRect(metrics.mScrollableRect);
} else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) {
mContentRect = metrics.mScrollableRect;
SetPageRect(mContentRect);
}
// We synchronise the viewport information with Java after sending the above
@ -470,26 +469,27 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const gfx3DMatr
gfxPoint fixedOffset;
gfxSize scaleDiff;
LayerRect content = LayerRect::FromCSSRect(mContentRect,
1 / aRootTransform.GetXScale(),
1 / aRootTransform.GetYScale());
// If the contents can fit entirely within the widget area on a particular
// dimenson, we need to translate and scale so that the fixed layers remain
// dimension, we need to translate and scale so that the fixed layers remain
// within the page boundaries.
if (mContentRect.width * tempScaleDiffX < metrics.mCompositionBounds.width) {
if (mContentRect.width * scaleX < metrics.mCompositionBounds.width) {
fixedOffset.x = -metricsScrollOffset.x;
scaleDiff.width = std::min(1.0f, metrics.mCompositionBounds.width / (float)mContentRect.width);
scaleDiff.width = std::min(1.0f, metrics.mCompositionBounds.width / content.width);
} else {
fixedOffset.x = clamped(scrollOffset.x / tempScaleDiffX, (float)mContentRect.x,
mContentRect.XMost() - metrics.mCompositionBounds.width / tempScaleDiffX) -
metricsScrollOffset.x;
fixedOffset.x = clamped(scrollOffset.x / tempScaleDiffX, content.x,
content.XMost() - metrics.mCompositionBounds.width / tempScaleDiffX) - metricsScrollOffset.x;
scaleDiff.width = tempScaleDiffX;
}
if (mContentRect.height * tempScaleDiffY < metrics.mCompositionBounds.height) {
if (mContentRect.height * scaleY < metrics.mCompositionBounds.height) {
fixedOffset.y = -metricsScrollOffset.y;
scaleDiff.height = std::min(1.0f, metrics.mCompositionBounds.height / (float)mContentRect.height);
scaleDiff.height = std::min(1.0f, metrics.mCompositionBounds.height / content.height);
} else {
fixedOffset.y = clamped(scrollOffset.y / tempScaleDiffY, (float)mContentRect.y,
mContentRect.YMost() - metrics.mCompositionBounds.height / tempScaleDiffY) -
metricsScrollOffset.y;
fixedOffset.y = clamped(scrollOffset.y / tempScaleDiffY, content.y,
content.YMost() - metrics.mCompositionBounds.height / tempScaleDiffY) - metricsScrollOffset.y;
scaleDiff.height = tempScaleDiffY;
}
@ -552,11 +552,10 @@ AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame)
void
AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
float aZoom,
const LayerIntRect& aPageRect,
const CSSRect& aCssPageRect)
{
#ifdef MOZ_WIDGET_ANDROID
AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aPageRect, aCssPageRect);
AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
#endif
}

View File

@ -116,7 +116,6 @@ private:
void SetFirstPaintViewport(const LayerIntPoint& aOffset,
float aZoom,
const LayerIntRect& aPageRect,
const CSSRect& aCssPageRect);
void SetPageRect(const CSSRect& aCssPageRect);
void SyncViewportInfo(const LayerIntRect& aDisplayPort,
@ -163,7 +162,7 @@ private:
void DetachRefLayers();
TargetConfig mTargetConfig;
LayerIntRect mContentRect;
CSSRect mContentRect;
nsRefPtr<LayerManagerComposite> mLayerManager;
// When this flag is set, the next composition will be the first for a

View File

@ -251,15 +251,17 @@ ThebesLayerComposite::GetCompositionBounds()
// Get the content document bounds, in screen-space.
const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics();
const LayerIntSize& contentSize = metrics.mContentRect.Size();
const LayerIntRect content = LayerIntRect::FromCSSRectRounded(metrics.mScrollableRect,
1 / scaleX,
1 / scaleY);
gfx::Point scrollOffset =
gfx::Point((metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().width) / scaleX,
(metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().height) / scaleY);
const nsIntPoint contentOrigin(
metrics.mContentRect.x - NS_lround(scrollOffset.x),
metrics.mContentRect.y - NS_lround(scrollOffset.y));
content.x - NS_lround(scrollOffset.x),
content.y - NS_lround(scrollOffset.y));
gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y,
contentSize.width, contentSize.height);
content.width, content.height);
gfxRect contentBounds = scrollableLayer->GetEffectiveTransform().
TransformBounds(contentRect);

View File

@ -832,19 +832,6 @@ void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) {
mFrameMetrics = metrics;
}
void AsyncPanZoomController::SetPageRect(const CSSRect& aCSSPageRect) {
FrameMetrics metrics = mFrameMetrics;
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
// The page rect is the css page rect scaled by the current zoom.
// Round the page rect so we don't get any truncation, then get the nsIntRect
// from this.
metrics.mContentRect = LayerRect::FromCSSRectRoundOut(aCSSPageRect, resolution, resolution);
metrics.mScrollableRect = aCSSPageRect;
mFrameMetrics = metrics;
}
void AsyncPanZoomController::ScaleWithFocus(float aZoom,
const nsIntPoint& aFocus) {
float zoomFactor = aZoom / mFrameMetrics.mZoom.width;
@ -852,10 +839,6 @@ void AsyncPanZoomController::ScaleWithFocus(float aZoom,
SetZoomAndResolution(aZoom);
// Force a recalculation of the page rect based on the new zoom and the
// current CSS page rect (which is unchanged since it's not affected by zoom).
SetPageRect(mFrameMetrics.mScrollableRect);
// If the new scale is very small, we risk multiplying in huge rounding
// errors, so don't bother adjusting the scroll offset.
if (resolution >= 0.01f) {
@ -1272,12 +1255,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
mFrameMetrics = aViewportFrame;
SetPageRect(mFrameMetrics.mScrollableRect);
mState = NOTHING;
} else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) {
mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect;
SetPageRect(mFrameMetrics.mScrollableRect);
}
if (needContentRepaint) {

View File

@ -198,12 +198,6 @@ public:
// These methods can be called from any thread.
//
/**
* Sets the CSS page rect, and calculates a new page rect based on the zoom
* level of the current metrics and the passed in CSS page rect.
*/
void SetPageRect(const CSSRect& aCSSPageRect);
/**
* Sets the DPI of the device for use within panning and zooming logic. It is
* a platform responsibility to set this on initialization of this class and

View File

@ -1074,7 +1074,6 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
{
WriteParam(aMsg, aParam.mScrollableRect);
WriteParam(aMsg, aParam.mViewport);
WriteParam(aMsg, aParam.mContentRect);
WriteParam(aMsg, aParam.mScrollOffset);
WriteParam(aMsg, aParam.mDisplayPort);
WriteParam(aMsg, aParam.mCriticalDisplayPort);
@ -1091,7 +1090,6 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
{
return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
ReadParam(aMsg, aIter, &aResult->mViewport) &&
ReadParam(aMsg, aIter, &aResult->mContentRect) &&
ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) &&

View File

@ -260,7 +260,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
unsigned staticLevel;
RootedValue thisv(cx);
if (evalType == DIRECT_EVAL) {
JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInIon());
JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInJit());
staticLevel = caller.script()->staticLevel + 1;
// Direct calls to eval are supposed to see the caller's |this|. If we

View File

@ -275,15 +275,15 @@ MarkRangeConservativelyAndSkipIon(JSTracer *trc, JSRuntime *rt, const uintptr_t
const uintptr_t *i = begin;
#if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION)
// Walk only regions in between Ion activations. Note that non-volatile
// registers are spilled to the stack before the entry Ion frame, ensuring
// Walk only regions in between JIT activations. Note that non-volatile
// registers are spilled to the stack before the entry frame, ensuring
// that the conservative scanner will still see them.
for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
uintptr_t *ionMin, *ionEnd;
ion.ionStackRange(ionMin, ionEnd);
for (ion::JitActivationIterator iter(rt); !iter.done(); ++iter) {
uintptr_t *jitMin, *jitEnd;
iter.jitStackRange(jitMin, jitEnd);
MarkRangeConservatively(trc, i, ionMin);
i = ionEnd;
MarkRangeConservatively(trc, i, jitMin);
i = jitEnd;
}
#endif
@ -742,7 +742,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
rt->stackSpace.mark(trc);
#ifdef JS_ION
ion::MarkIonActivations(rt, trc);
ion::MarkJitActivations(rt, trc);
#endif
for (CompartmentsIter c(rt); !c.done(); c.next())

View File

@ -119,7 +119,7 @@ CheckStackRootsRange(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rb
}
static void
CheckStackRootsRangeAndSkipIon(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
CheckStackRootsRangeAndSkipJit(JSRuntime *rt, uintptr_t *begin, uintptr_t *end, Rooter *rbegin, Rooter *rend)
{
/*
* Regions of the stack between Ion activiations are marked exactly through
@ -129,16 +129,16 @@ CheckStackRootsRangeAndSkipIon(JSRuntime *rt, uintptr_t *begin, uintptr_t *end,
uintptr_t *i = begin;
#if defined(JS_ION)
for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
uintptr_t *ionMin, *ionEnd;
ion.ionStackRange(ionMin, ionEnd);
for (ion::JitActivationIterator iter(rt); !iter.done(); ++iter) {
uintptr_t *jitMin, *jitEnd;
iter.jitStackRange(jitMin, jitEnd);
uintptr_t *upto = Min(ionMin, end);
uintptr_t *upto = Min(jitMin, end);
if (upto > i)
CheckStackRootsRange(rt, i, upto, rbegin, rend);
else
break;
i = ionEnd;
i = jitEnd;
}
#endif
@ -296,7 +296,7 @@ JS::CheckStackRoots(JSContext *cx)
}
}
CheckStackRootsRangeAndSkipIon(rt, stackMin, stackEnd, firstToScan, rooters.end());
CheckStackRootsRangeAndSkipJit(rt, stackMin, stackEnd, firstToScan, rooters.end());
CheckStackRootsRange(rt, cgcd->registerSnapshot.words,
ArrayEnd(cgcd->registerSnapshot.words),
firstToScan, rooters.end());

View File

@ -69,9 +69,9 @@ ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
JSContext *cx = GetIonContext()->cx;
// We don't have an exit frame.
cx->mainThread().ionTop = NULL;
IonActivationIterator ionActivations(cx);
IonBailoutIterator iter(ionActivations, sp);
IonActivation *activation = ionActivations.activation();
JitActivationIterator jitActivations(cx->runtime());
IonBailoutIterator iter(jitActivations, sp);
JitActivation *activation = jitActivations.activation()->asJit();
IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
@ -100,9 +100,9 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
// We don't have an exit frame.
cx->mainThread().ionTop = NULL;
IonActivationIterator ionActivations(cx);
IonBailoutIterator iter(ionActivations, sp);
IonActivation *activation = ionActivations.activation();
JitActivationIterator jitActivations(cx->runtime());
IonBailoutIterator iter(jitActivations, sp);
JitActivation *activation = jitActivations.activation()->asJit();
IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());

View File

@ -122,8 +122,8 @@ class IonBailoutIterator : public IonFrameIterator
IonScript *topIonScript_;
public:
IonBailoutIterator(const IonActivationIterator &activations, BailoutStack *sp);
IonBailoutIterator(const IonActivationIterator &activations, InvalidationBailoutStack *sp);
IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
SnapshotOffset snapshotOffset() const {
JS_ASSERT(topIonScript_);

View File

@ -999,7 +999,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
}
uint32_t
ion::BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIterator &iter,
ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
bool invalidate, BaselineBailoutInfo **bailoutInfo)
{
JS_ASSERT(bailoutInfo != NULL);
@ -1214,7 +1214,10 @@ ion::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
// that script->needsArgsObj() implies frame->hasArgsObj().
RootedScript innerScript(cx, NULL);
RootedScript outerScript(cx, NULL);
IonFrameIterator iter(cx);
JS_ASSERT(cx->mainThread().currentlyRunningInJit());
IonFrameIterator iter(cx->mainThread().ionTop);
uint32_t frameno = 0;
while (frameno < numFrames) {
JS_ASSERT(!iter.isOptimizedJS());

View File

@ -676,7 +676,9 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Reg
static bool
IsTopFrameConstructing(JSContext *cx)
{
IonFrameIterator iter(cx);
JS_ASSERT(cx->mainThread().currentlyRunningInJit());
JitActivationIterator activations(cx->runtime());
IonFrameIterator iter(activations);
JS_ASSERT(iter.type() == IonFrame_Exit);
++iter;

View File

@ -116,9 +116,11 @@ EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr)
{
AssertCompartmentUnchanged pcc(cx);
IonContext ictx(cx, NULL);
IonActivation activation(cx, fp);
JitActivation activation(cx, fp->isConstructing());
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
fp->setRunningInJit();
// Pass the scope chain for global and eval frames.
JSObject *scopeChain = NULL;
if (!fp->isNonEvalFunctionFrame())
@ -132,6 +134,8 @@ EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr)
// Single transition point from Interpreter to Baseline.
enter(jitcode, maxArgc, maxArgv, osr ? fp : NULL, calleeToken, scopeChain, numStackValues,
result.address());
fp->clearRunningInJit();
}
JS_ASSERT(fp == cx->fp());
@ -813,7 +817,7 @@ ion::ToggleBaselineSPS(JSRuntime *runtime, bool enable)
}
static void
MarkActiveBaselineScripts(JSContext *cx, const IonActivationIterator &activation)
MarkActiveBaselineScripts(JSContext *cx, const JitActivationIterator &activation)
{
for (ion::IonFrameIterator iter(activation); !iter.done(); ++iter) {
switch (iter.type()) {
@ -836,10 +840,10 @@ MarkActiveBaselineScripts(JSContext *cx, const IonActivationIterator &activation
void
ion::MarkActiveBaselineScripts(Zone *zone)
{
// First check if there is an IonActivation on the stack, so that there
// First check if there is a JitActivation on the stack, so that there
// must be a valid IonContext.
IonActivationIterator iter(zone->rt);
if (!iter.more())
JitActivationIterator iter(zone->rt);
if (iter.done())
return;
// If baseline is disabled, there are no baseline scripts on the stack.
@ -847,7 +851,7 @@ ion::MarkActiveBaselineScripts(Zone *zone)
if (!ion::IsBaselineEnabled(cx))
return;
for (; iter.more(); ++iter) {
for (; !iter.done(); ++iter) {
if (iter.activation()->compartment()->zone() == zone)
MarkActiveBaselineScripts(cx, iter);
}

View File

@ -319,7 +319,7 @@ struct BaselineBailoutInfo
};
uint32_t
BailoutIonToBaseline(JSContext *cx, IonActivation *activation, IonBailoutIterator &iter,
BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
bool invalidate, BaselineBailoutInfo **bailoutInfo);
// Mark baseline scripts on the stack as active, so that they are not discarded

View File

@ -408,32 +408,6 @@ IonCompartment::getVMWrapper(const VMFunction &f)
return p->value;
}
IonActivation::IonActivation(JSContext *cx, StackFrame *fp)
: cx_(cx),
compartment_(cx->compartment()),
prev_(cx->mainThread().ionActivation),
entryfp_(fp),
prevIonTop_(cx->mainThread().ionTop),
prevIonJSContext_(cx->mainThread().ionJSContext),
prevpc_(NULL)
{
if (fp)
fp->setRunningInIon();
cx->mainThread().ionJSContext = cx;
cx->mainThread().ionActivation = this;
}
IonActivation::~IonActivation()
{
JS_ASSERT(cx_->mainThread().ionActivation == this);
if (entryfp_)
entryfp_->clearRunningInIon();
cx_->mainThread().ionActivation = prev();
cx_->mainThread().ionTop = prevIonTop_;
cx_->mainThread().ionJSContext = prevIonJSContext_;
}
IonCode *
IonCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
{
@ -1377,7 +1351,7 @@ IonCompile(JSContext *cx, JSScript *script,
AutoFlushCache afc("IonCompile");
types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
if (!enterCompiler.init(script, false, 0))
if (!enterCompiler.init(script))
return AbortReason_Disable;
AutoTempAllocatorRooter root(cx, temp);
@ -1836,12 +1810,17 @@ EnterIon(JSContext *cx, StackFrame *fp, void *jitcode)
{
AssertCompartmentUnchanged pcc(cx);
IonContext ictx(cx, NULL);
IonActivation activation(cx, fp);
JitActivation activation(cx, fp->isConstructing());
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
fp->setRunningInJit();
// Single transition point from Interpreter to Ion.
enter(jitcode, maxArgc, maxArgv, fp, calleeToken, /* scopeChain = */ NULL, 0,
result.address());
fp->clearRunningInJit();
}
JS_ASSERT(fp == cx->fp());
@ -1901,29 +1880,7 @@ ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args)
JS_ASSERT(ion::IsEnabled(cx));
JS_ASSERT(!ion->bailoutExpected());
bool clearCallingIntoIon = false;
StackFrame *fp = cx->fp();
// Two cases we have to handle:
//
// (1) fp does not begin an Ion activation. This works exactly
// like invoking Ion from JM: entryfp is set to fp and fp
// has the callingIntoIon flag set.
//
// (2) fp already begins another IonActivation, for instance:
// JM -> Ion -> array_sort -> Ion
// In this cas we use an IonActivation with entryfp == NULL
// and prevpc != NULL.
IonActivation activation(cx, NULL);
if (!fp->beginsIonActivation()) {
fp->setCallingIntoIon();
clearCallingIntoIon = true;
activation.setEntryFp(fp);
} else {
JS_ASSERT(!activation.entryfp());
}
activation.setPrevPc(cx->regs().pc);
JitActivation activation(cx, /* firstFrameIsConstructing = */false);
EnterIonCode enter = cx->compartment()->ionCompartment()->enterJIT();
void *calleeToken = CalleeToToken(fun);
@ -1932,13 +1889,9 @@ ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args)
JS_ASSERT(args.length() >= fun->nargs);
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
enter(jitcode, args.length() + 1, args.array() - 1, fp, calleeToken,
enter(jitcode, args.length() + 1, args.array() - 1, NULL, calleeToken,
/* scopeChain = */ NULL, 0, result.address());
if (clearCallingIntoIon)
fp->clearCallingIntoIon();
JS_ASSERT(fp == cx->fp());
JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
args.rval().set(result);
@ -2083,12 +2036,12 @@ ion::InvalidateAll(FreeOp *fop, Zone *zone)
FinishAllOffThreadCompilations(comp->ionCompartment());
}
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
if (iter.activation()->compartment()->zone() == zone) {
IonContext ictx(zone->rt);
AutoFlushCache afc("InvalidateAll", zone->rt->ionRuntime());
IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
InvalidateActivation(fop, iter.top(), true);
InvalidateActivation(fop, iter.jitTop(), true);
}
}
}
@ -2126,8 +2079,8 @@ ion::Invalidate(types::TypeCompartment &types, FreeOp *fop,
return;
}
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter)
InvalidateActivation(fop, iter.top(), false);
for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter)
InvalidateActivation(fop, iter.jitTop(), false);
// Drop the references added above. If a script was never active, its
// IonScript will be immediately destroyed. Otherwise, it will be held live

View File

@ -3409,6 +3409,16 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
RootedScript calleeScript(cx, target->nonLazyScript());
BaselineInspector inspector(cx, target->nonLazyScript());
// Improve type information of |this| when not set.
if (callInfo.constructing() && !callInfo.thisArg()->resultTypeSet()) {
types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types), Bailout_Normal);
current->add(barrier);
MUnbox *unbox = MUnbox::New(barrier, MIRType_Object, MUnbox::Infallible);
current->add(unbox);
callInfo.setThis(unbox);
}
// Start inlining.
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
CompileInfo *info = alloc->new_<CompileInfo>(calleeScript.get(), target,

View File

@ -28,7 +28,6 @@ typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
CalleeToken calleeToken, JSObject *scopeChain,
size_t numStackValues, Value *vp);
class IonActivation;
class IonBuilder;
typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
@ -175,7 +174,7 @@ class IonRuntime
class IonCompartment
{
friend class IonActivation;
friend class JitActivation;
// Ion state for the compartment's runtime.
IonRuntime *rt;
@ -310,63 +309,6 @@ class IonCompartment
}
};
class IonActivation
{
private:
JSContext *cx_;
JSCompartment *compartment_;
IonActivation *prev_;
StackFrame *entryfp_;
uint8_t *prevIonTop_;
JSContext *prevIonJSContext_;
// When creating an activation without a StackFrame, this field is used
// to communicate the calling pc for ScriptFrameIter.
jsbytecode *prevpc_;
public:
IonActivation(JSContext *cx, StackFrame *fp);
~IonActivation();
StackFrame *entryfp() const {
return entryfp_;
}
IonActivation *prev() const {
return prev_;
}
uint8_t *prevIonTop() const {
return prevIonTop_;
}
jsbytecode *prevpc() const {
JS_ASSERT_IF(entryfp_, entryfp_->callingIntoIon());
return prevpc_;
}
void setEntryFp(StackFrame *fp) {
JS_ASSERT_IF(fp, !entryfp_);
entryfp_ = fp;
}
void setPrevPc(jsbytecode *pc) {
JS_ASSERT_IF(pc, !prevpc_);
prevpc_ = pc;
}
JSCompartment *compartment() const {
return compartment_;
}
bool empty() const {
// If we have an entryfp, this activation is active. However, if
// FastInvoke is used, entryfp may be NULL and a non-NULL prevpc
// indicates this activation is not empty.
return !entryfp_ && !prevpc_;
}
static inline size_t offsetOfPrevPc() {
return offsetof(IonActivation, prevpc_);
}
static inline size_t offsetOfEntryFp() {
return offsetof(IonActivation, entryfp_);
}
};
// Called from JSCompartment::discardJitCode().
void InvalidateAll(FreeOp *fop, JS::Zone *zone);
void FinishInvalidation(FreeOp *fop, JSScript *script);

View File

@ -14,6 +14,10 @@
class JSFunction;
class JSScript;
namespace js {
class ActivationIterator;
};
namespace js {
namespace ion {
@ -52,7 +56,7 @@ enum FrameType
// An exit frame is necessary for transitioning from a JS frame into C++.
// From within C++, an exit frame is always the last frame in any
// IonActivation.
// JitActivation.
IonFrame_Exit,
// An OSR frame is added when performing OSR from within a bailout. It
@ -65,11 +69,10 @@ class IonCommonFrameLayout;
class IonJSFrameLayout;
class IonExitFrameLayout;
class IonActivation;
class IonActivationIterator;
class BaselineFrame;
class JitActivation;
class IonFrameIterator
{
protected:
@ -80,7 +83,7 @@ class IonFrameIterator
private:
mutable const SafepointIndex *cachedSafepointIndex_;
const IonActivation *activation_;
const JitActivation *activation_;
void dumpBaseline() const;
@ -94,7 +97,7 @@ class IonFrameIterator
activation_(NULL)
{ }
IonFrameIterator(const IonActivationIterator &activations);
IonFrameIterator(const ActivationIterator &activations);
IonFrameIterator(IonJSFrameLayout *fp);
// Current frame information.
@ -203,32 +206,6 @@ class IonFrameIterator
inline BaselineFrame *baselineFrame() const;
};
class IonActivationIterator
{
uint8_t *top_;
IonActivation *activation_;
private:
void settle();
public:
IonActivationIterator(JSContext *cx);
IonActivationIterator(JSRuntime *rt);
IonActivationIterator &operator++();
IonActivation *activation() const {
return activation_;
}
uint8_t *top() const {
return top_;
}
bool more() const;
// Returns the bottom and top addresses of the current activation.
void ionStackRange(uintptr_t *&min, uintptr_t *&end);
};
class IonJSFrameLayout;
class IonBailoutIterator;

View File

@ -30,13 +30,13 @@
namespace js {
namespace ion {
IonFrameIterator::IonFrameIterator(const IonActivationIterator &activations)
: current_(activations.top()),
IonFrameIterator::IonFrameIterator(const ActivationIterator &activations)
: current_(activations.jitTop()),
type_(IonFrame_Exit),
returnAddressToFp_(NULL),
frameSize_(0),
cachedSafepointIndex_(NULL),
activation_(activations.activation())
activation_(activations.activation()->asJit())
{
}
@ -597,45 +597,6 @@ EnsureExitFrame(IonCommonFrameLayout *frame)
frame->changePrevType(IonFrame_Unwound_OptimizedJS);
}
void
IonActivationIterator::settle()
{
while (activation_ && activation_->empty()) {
top_ = activation_->prevIonTop();
activation_ = activation_->prev();
}
}
IonActivationIterator::IonActivationIterator(JSContext *cx)
: top_(cx->mainThread().ionTop),
activation_(cx->mainThread().ionActivation)
{
settle();
}
IonActivationIterator::IonActivationIterator(JSRuntime *rt)
: top_(rt->mainThread.ionTop),
activation_(rt->mainThread.ionActivation)
{
settle();
}
IonActivationIterator &
IonActivationIterator::operator++()
{
JS_ASSERT(activation_);
top_ = activation_->prevIonTop();
activation_ = activation_->prev();
settle();
return *this;
}
bool
IonActivationIterator::more() const
{
return !!activation_;
}
CalleeToken
MarkCalleeToken(JSTracer *trc, CalleeToken token)
{
@ -790,9 +751,9 @@ MarkBaselineStubFrame(JSTracer *trc, const IonFrameIterator &frame)
}
void
IonActivationIterator::ionStackRange(uintptr_t *&min, uintptr_t *&end)
JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
{
IonFrameIterator frames(top());
IonFrameIterator frames(jitTop());
if (frames.isFakeExitFrame()) {
min = reinterpret_cast<uintptr_t *>(frames.fp());
@ -969,7 +930,7 @@ MarkIonExitFrame(JSTracer *trc, const IonFrameIterator &frame)
}
static void
MarkIonActivation(JSTracer *trc, const IonActivationIterator &activations)
MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
{
for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
switch (frames.type()) {
@ -1004,10 +965,10 @@ MarkIonActivation(JSTracer *trc, const IonActivationIterator &activations)
}
void
MarkIonActivations(JSRuntime *rt, JSTracer *trc)
MarkJitActivations(JSRuntime *rt, JSTracer *trc)
{
for (IonActivationIterator activations(rt); activations.more(); ++activations)
MarkIonActivation(trc, activations);
for (JitActivationIterator activations(rt); !activations.done(); ++activations)
MarkJitActivation(trc, activations);
}
void
@ -1020,7 +981,6 @@ AutoTempAllocatorRooter::trace(JSTracer *trc)
void
GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
{
JS_ASSERT(cx->fp()->beginsIonActivation());
IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
JSRuntime *rt = cx->runtime();
@ -1400,19 +1360,7 @@ IonFrameIterator::isConstructing() const
}
JS_ASSERT(parent.done());
// If entryfp is not set, we entered Ion via a C++ native, like Array.map,
// using FastInvoke. FastInvoke is never used for constructor calls.
if (!activation_->entryfp())
return false;
// If callingIntoIon, we either entered Ion from JM or entered Ion from
// a C++ native using FastInvoke. In both of these cases we don't handle
// constructor calls.
if (activation_->entryfp()->callingIntoIon())
return false;
JS_ASSERT(activation_->entryfp()->runningInIon());
return activation_->entryfp()->isConstructing();
return activation_->firstFrameIsConstructing();
}
unsigned

View File

@ -270,7 +270,7 @@ void HandleParallelFailure(ResumeFromException *rfe);
void EnsureExitFrame(IonCommonFrameLayout *frame);
void MarkIonActivations(JSRuntime *rt, JSTracer *trc);
void MarkJitActivations(JSRuntime *rt, JSTracer *trc);
void MarkIonCompilerRoots(JSTracer *trc);
static inline uint32_t

View File

@ -215,9 +215,10 @@ class MacroAssembler : public MacroAssemblerSpecific
movePtr(ImmWord(GetIonContext()->runtime), dest);
loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionJSContext)), dest);
}
void loadIonActivation(const Register &dest) {
void loadJitActivation(const Register &dest) {
movePtr(ImmWord(GetIonContext()->runtime), dest);
loadPtr(Address(dest, offsetof(JSRuntime, mainThread.ionActivation)), dest);
size_t offset = offsetof(JSRuntime, mainThread) + PerThreadData::offsetOfActivation();
loadPtr(Address(dest, offset), dest);
}
template<typename T>

View File

@ -112,7 +112,7 @@ class BailoutStack
} // namespace ion
} // namespace js
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
BailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machine())
@ -131,7 +131,7 @@ IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
}
// Compute the snapshot offset from the bailout ID.
IonActivation *activation = activations.activation();
JitActivation *activation = activations.activation()->asJit();
JSCompartment *jsCompartment = activation->compartment();
IonCompartment *ionCompartment = jsCompartment->ionCompartment();
IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass());
@ -148,7 +148,7 @@ IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
}
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
InvalidationBailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machine())

View File

@ -406,6 +406,8 @@ VirtualRegisterOfPayload(MDefinition *mir)
if (!inner->isConstant() && inner->type() != MIRType_Double)
return inner->virtualRegister();
}
if (mir->isTypeBarrier())
return VirtualRegisterOfPayload(mir->getOperand(0));
return mir->virtualRegister() + VREG_DATA_OFFSET;
}

View File

@ -49,7 +49,7 @@ class BailoutStack
# pragma pack(pop)
#endif
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
BailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machineState())
@ -64,7 +64,7 @@ IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
snapshotOffset_ = bailout->snapshotOffset();
}
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
InvalidationBailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machine())

View File

@ -65,7 +65,7 @@ class BailoutStack
# pragma pack(pop)
#endif
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
BailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machine())
@ -84,7 +84,7 @@ IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
}
// Compute the snapshot offset from the bailout ID.
IonActivation *activation = activations.activation();
JitActivation *activation = activations.activation()->asJit();
JSCompartment *jsCompartment = activation->compartment();
IonCompartment *ionCompartment = jsCompartment->ionCompartment();
IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass());
@ -101,7 +101,7 @@ IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId);
}
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations,
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
InvalidationBailoutStack *bailout)
: IonFrameIterator(activations),
machine_(bailout->machine())

View File

@ -0,0 +1,17 @@
// Error().stack (ScriptFrameIter) should not include frames from other contexts.
function g() {
evaluate("function h() {\nstack = Error().stack;\n };\n h();", {newContext: true});
}
function f() {
g();
}
f();
assertEq(stack,
"h@@evaluate:2\n" +
"@@evaluate:4\n");
function k() {
evaluate("stack = Error().stack", {newContext: true});
}
k();
assertEq(stack, "@@evaluate:1\n");

View File

@ -716,7 +716,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
ionTop(NULL),
ionJSContext(NULL),
ionStackLimit(0),
ionActivation(NULL),
activation_(NULL),
asmJSActivationStack_(NULL),
suppressGC(0)
{}

View File

@ -1262,6 +1262,9 @@ JSContext::saveFrameChain()
return false;
}
if (Activation *act = mainThread().activation())
act->saveFrameChain();
if (defaultCompartmentObject_)
setCompartment(defaultCompartmentObject_->compartment());
else
@ -1282,6 +1285,9 @@ JSContext::restoreFrameChain()
stack.restoreFrameChain();
if (Activation *act = mainThread().activation())
act->restoreFrameChain();
if (isExceptionPending())
wrapPendingException();
}

View File

@ -129,7 +129,6 @@ class MathCache;
namespace ion {
class IonRuntime;
class IonActivation;
}
class WeakMapBase;
@ -488,11 +487,6 @@ class PerThreadData : public js::PerThreadDataFriendFields
inline void setIonStackLimit(uintptr_t limit);
/*
* This points to the most recent Ion activation running on the thread.
*/
js::ion::IonActivation *ionActivation;
/*
* asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This
* stack is used by JSRuntime::triggerOperationCallback to stop long-
@ -502,12 +496,23 @@ class PerThreadData : public js::PerThreadDataFriendFields
* synchronized (by rt->operationCallbackLock).
*/
private:
friend class js::Activation;
friend class js::ActivationIterator;
friend class js::AsmJSActivation;
/*
* Points to the most recent activation running on the thread.
* See Activation comment in vm/Stack.h.
*/
js::Activation *activation_;
/* See AsmJSActivation comment. Protected by rt->operationCallbackLock. */
js::AsmJSActivation *asmJSActivationStack_;
public:
static unsigned offsetOfActivation() {
return offsetof(PerThreadData, activation_);
}
static unsigned offsetOfAsmJSActivationStackReadOnly() {
return offsetof(PerThreadData, asmJSActivationStack_);
}
@ -519,6 +524,16 @@ class PerThreadData : public js::PerThreadDataFriendFields
return asmJSActivationStack_;
}
js::Activation *activation() const {
return activation_;
}
bool currentlyRunningInInterpreter() const {
return activation_->isInterpreter();
}
bool currentlyRunningInJit() const {
return activation_->isJit();
}
/*
* When this flag is non-zero, any attempt to GC will be skipped. It is used
* to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in

View File

@ -595,21 +595,11 @@ JSCompartment::purge()
bool
JSCompartment::hasScriptsOnStack()
{
for (AllFramesIter afi(rt); !afi.done(); ++afi) {
#ifdef JS_ION
// If this is an Ion frame, check the IonActivation instead
if (afi.isIon())
continue;
#endif
if (afi.interpFrame()->script()->compartment() == this)
for (ActivationIterator iter(rt); !iter.done(); ++iter) {
if (iter.activation()->compartment() == this)
return true;
}
#ifdef JS_ION
for (ion::IonActivationIterator iai(rt); iai.more(); ++iai) {
if (iai.activation()->compartment() == this)
return true;
}
#endif
return false;
}

View File

@ -1059,7 +1059,7 @@ FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int
RootedObject callObj(cx);
AutoPropertyDescArray callProps(cx);
if (!iter.isIon() && (showArgs || showLocals)) {
if (!iter.isJit() && (showArgs || showLocals)) {
JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
callObj = frame.callObject(cx);
if (callObj)

View File

@ -876,18 +876,15 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
/*
* Pretend we have been passed the 'arguments' object for the current
* function and read actuals out of the frame.
*
* N.B. Changes here need to be propagated to stubs::SplatApplyArgs.
*/
/* Steps 4-6. */
StackFrame *fp = cx->fp();
#ifdef JS_ION
// We do not want to use ScriptFrameIter to abstract here because this
// is supposed to be a fast path as opposed to ScriptFrameIter which is
// doing complex logic to settle on the next frame twice.
if (fp->beginsIonActivation()) {
ion::IonActivationIterator activations(cx);
if (cx->mainThread().currentlyRunningInJit()) {
ion::JitActivationIterator activations(cx->runtime());
ion::IonFrameIterator frame(activations);
if (frame.isNative()) {
// Stop on the next Ion JS Frame.
@ -931,6 +928,7 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
} else
#endif
{
StackFrame *fp = cx->fp();
unsigned length = fp->numActualArgs();
JS_ASSERT(length <= StackSpace::ARGS_LENGTH_MAX);

View File

@ -2324,49 +2324,15 @@ StackTypeSet::propertyNeedsBarrier(JSContext *cx, jsid id)
return false;
}
enum RecompileKind {
RECOMPILE_CHECK_MONITORED,
RECOMPILE_CHECK_BARRIERS,
RECOMPILE_NONE
};
/*
* Whether all jitcode for a given pc was compiled with monitoring or barriers.
* If we reanalyze the script after generating jitcode, new monitoring and
* barriers will be added which may be duplicating information available when
* the script was originally compiled, and which should not invalidate that
* compilation.
*/
static inline bool
JITCodeHasCheck(JSScript *script, jsbytecode *pc, RecompileKind kind)
{
if (kind == RECOMPILE_NONE)
return false;
if (script->hasAnyIonScript() ||
script->isIonCompilingOffThread() ||
script->isParallelIonCompilingOffThread())
{
return false;
}
return true;
}
/*
* Force recompilation of any jitcode for script at pc, or of any other script
* Force recompilation of any jitcode for the script, or of any other script
* which this script was inlined into.
*/
static inline void
AddPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc,
RecompileKind kind = RECOMPILE_NONE)
AddPendingRecompile(JSContext *cx, JSScript *script)
{
/*
* Trigger recompilation of the script itself, if code was not previously
* compiled with the specified information.
*/
if (!JITCodeHasCheck(script, pc, kind))
cx->compartment()->types.addPendingRecompile(cx, script, pc);
/* Trigger recompilation of the script itself. */
cx->compartment()->types.addPendingRecompile(cx, script);
/*
* Remind Ion not to save the compile code if generating type
@ -2422,7 +2388,7 @@ class TypeConstraintFreezeStack : public TypeConstraint
* not disable it on future changes to the type set.
*/
RootedScript script(cx, script_);
AddPendingRecompile(cx, script, NULL);
AddPendingRecompile(cx, script);
}
};
@ -2793,17 +2759,8 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop)
JS_ASSERT(!pending->empty());
#ifdef JS_ION
for (unsigned i = 0; i < pending->length(); i++) {
CompilerOutput &co = *(*pending)[i].compilerOutput(*this);
switch (co.kind()) {
case CompilerOutput::Ion:
case CompilerOutput::ParallelIon:
break;
}
}
ion::Invalidate(*this, fop, *pending);
#endif /* JS_ION */
#endif
fop->delete_(pending);
}
@ -2915,7 +2872,7 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
}
void
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
{
JS_ASSERT(script);
if (!constrainedOutputs)
@ -2965,7 +2922,7 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offse
code.monitoredTypes = true;
AddPendingRecompile(cx, script, pc, RECOMPILE_CHECK_MONITORED);
AddPendingRecompile(cx, script);
}
void
@ -3053,7 +3010,7 @@ ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *tar
* if any of the barriers is ever violated).
*/
RootedScript script(cx, script_);
AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
AddPendingRecompile(cx, script);
}
/* Ignore duplicate barriers. */
@ -3112,7 +3069,7 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ
if (!code.typeBarriers) {
/* Trigger recompilation as for normal type barriers. */
RootedScript script(cx, script_);
AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
AddPendingRecompile(cx, script);
}
InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
@ -3163,8 +3120,6 @@ TypeCompartment::print(JSContext *cx, bool force)
printf("%u", typeCounts[count]);
}
printf(" (%u over)\n", typeCountOver);
printf("Recompilations: %u\n", recompilations);
}
/////////////////////////////////////////////////////////////////////
@ -5549,7 +5504,7 @@ types::MarkIteratorUnknownSlow(JSContext *cx)
result->next = script->types->dynamicList;
script->types->dynamicList = result;
AddPendingRecompile(cx, script, NULL);
AddPendingRecompile(cx, script);
if (!script->hasAnalysis() || !script->analysis()->ranInference())
return;
@ -5662,42 +5617,7 @@ types::TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type t
result->next = script->types->dynamicList;
script->types->dynamicList = result;
/*
* New type information normally requires all code in the entire script to
* be recompiled, as changes to types can flow through variables etc. into
* other chunks in the compiled script.
*
* We can do better than this, though, when we can prove the new type will
* only be visible at certain points in the script. Namely, for arithmetic
* operations which might produce doubles and are then passed to an
* expression that cancels out integer overflow, i.e.'OP & -1' or 'OP | 0',
* the new type will only affect OP and the bitwise operation.
*
* This can prevent a significant amount of recompilation in scripts which
* use these operations extensively, principally autotranslated code.
*/
jsbytecode *ignorePC = pc + GetBytecodeLength(pc);
if (*ignorePC == JSOP_POP) {
/* Value is ignored. */
} else if (*ignorePC == JSOP_INT8 && GET_INT8(ignorePC) == -1) {
ignorePC += JSOP_INT8_LENGTH;
if (*ignorePC != JSOP_BITAND)
ignorePC = NULL;
} else if (*ignorePC == JSOP_ZERO) {
ignorePC += JSOP_ZERO_LENGTH;
if (*ignorePC != JSOP_BITOR)
ignorePC = NULL;
} else {
ignorePC = NULL;
}
if (ignorePC) {
AddPendingRecompile(cx, script, pc);
AddPendingRecompile(cx, script, ignorePC);
} else {
AddPendingRecompile(cx, script, NULL);
}
AddPendingRecompile(cx, script);
if (script->hasAnalysis() && script->analysis()->ranInference()) {
TypeSet *pushed = script->analysis()->pushedTypes(pc, 0);

View File

@ -1291,10 +1291,7 @@ struct CompilerOutput
// but, for portability, bitfields are limited to bool, int, and
// unsigned int. You should really use the accessor below.
unsigned kindInt : 2;
bool constructing : 1;
bool barriers : 1;
bool pendingRecompilation : 1;
uint32_t chunkIndex:27;
CompilerOutput();
@ -1364,14 +1361,6 @@ struct TypeCompartment
/* Pending recompilations to perform before execution of JIT code can resume. */
Vector<RecompileInfo> *pendingRecompiles;
/*
* Number of recompilation events and inline frame expansions that have
* occurred in this compartment. If these change, code should not count on
* compiled code or the current stack being intact.
*/
unsigned recompilations;
unsigned frameExpansions;
/*
* Script currently being compiled. All constraints which look for type
* changes inducing recompilation are keyed to this script. Note: script
@ -1433,7 +1422,7 @@ struct TypeCompartment
/* Mark a script as needing recompilation once inference has finished. */
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);
void addPendingRecompile(JSContext *cx, JSScript *script);
/* Monitor future effects on a bytecode. */
void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,

View File

@ -92,10 +92,7 @@ namespace types {
inline
CompilerOutput::CompilerOutput()
: script(NULL),
kindInt(Ion),
constructing(false),
barriers(false),
chunkIndex(false)
kindInt(Ion)
{
}
@ -394,14 +391,11 @@ struct AutoEnterCompilation
JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning);
}
bool init(JSScript *script, bool constructing, unsigned chunkIndex)
bool init(JSScript *script)
{
CompilerOutput co;
co.script = script;
co.setKind(kind);
co.constructing = constructing;
co.barriers = cx->zone()->compileBarriers();
co.chunkIndex = chunkIndex;
// This flag is used to prevent adding the current compiled script in
// the list of compiler output which should be invalided. This is
@ -701,16 +695,6 @@ extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
js::types::Type type);
inline bool
UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
{
if (!fp->isConstructing() || !cx->typeInferenceEnabled() || !fp->prev())
return false;
JSScript *prevScript = fp->prev()->script();
return UseNewType(cx, prevScript, fp->prevpc());
}
inline bool
UseNewTypeForClone(JSFunction *fun)
{
@ -987,14 +971,7 @@ TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
/* static */ inline void
TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
{
#ifdef JS_ION
if (cx->fp()->beginsIonActivation()) {
ion::GetPcScript(cx, script, pc);
return;
}
#endif
*script = cx->fp()->script();
*pc = cx->regs().pc;
*script = cx->stack.currentScript(pc);
}
/* static */ inline void

View File

@ -5221,7 +5221,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
return;
}
} else {
while (!i.done() && !i.isIon() && i.interpFrame() != start)
while (!i.done() && !i.isJit() && i.interpFrame() != start)
++i;
if (i.done()) {
@ -5232,8 +5232,8 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
}
for (; !i.done(); ++i) {
if (i.isIon())
fprintf(stderr, "IonFrame\n");
if (i.isJit())
fprintf(stderr, "JIT frame\n");
else
fprintf(stderr, "StackFrame at %p\n", (void *) i.interpFrame());
@ -5252,10 +5252,10 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
fprintf(stderr, " pc = %p\n", pc);
fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
}
if (!i.isIon())
if (!i.isJit())
MaybeDumpObject("blockChain", i.interpFrame()->maybeBlockChain());
MaybeDumpValue("this", i.thisv());
if (!i.isIon()) {
if (!i.isJit()) {
fprintf(stderr, " rval: ");
dumpValue(i.interpFrame()->returnValue());
fputc('\n', stderr);
@ -5264,13 +5264,13 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start)
fprintf(stderr, " flags:");
if (i.isConstructing())
fprintf(stderr, " constructing");
if (!i.isIon() && i.interpFrame()->isDebuggerFrame())
if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
fprintf(stderr, " debugger");
if (i.isEvalFrame())
fprintf(stderr, " eval");
if (!i.isIon() && i.interpFrame()->isYielding())
if (!i.isJit() && i.interpFrame()->isYielding())
fprintf(stderr, " yielding");
if (!i.isIon() && i.interpFrame()->isGeneratorFrame())
if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
fprintf(stderr, " generator");
fputc('\n', stderr);
@ -5293,7 +5293,7 @@ js_DumpBacktrace(JSContext *cx)
unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
JSScript *script = i.script();
sprinter.printf("#%d %14p %s:%d (%p @ %d)\n",
depth, (i.isIon() ? 0 : i.interpFrame()), filename, line,
depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
script, i.pc() - script->code);
}
fprintf(stdout, "%s", sprinter.string());

View File

@ -1462,10 +1462,10 @@ FindStartPC(JSContext *cx, ScriptFrameIter &iter, int spindex, int skipStackHits
* stack pointer and skewing it from what static analysis in pcstack.init
* would compute.
*
* FIXME: also fall back if iter.isIonOptimizedJS(), since the stack snapshot
* may be for the previous pc (see bug 831120).
* FIXME: also fall back if iter.isIon(), since the stack snapshot may be
* for the previous pc (see bug 831120).
*/
if (iter.isIonOptimizedJS())
if (iter.isIon())
return true;
*valuepc = NULL;

View File

@ -2885,17 +2885,17 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
* assumption of !script->needsArgsObj();
* - type inference data for the script assuming script->needsArgsObj
*/
for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
for (AllFramesIter i(cx); !i.done(); ++i) {
/*
* We cannot reliably create an arguments object for Ion activations of
* this script. To maintain the invariant that "script->needsArgsObj
* implies fp->hasArgsObj", the Ion bail mechanism will create an
* arguments object right after restoring the StackFrame and before
* entering the interpreter (in ion::ThunkToInterpreter). This delay is
* safe since the engine avoids any observation of a StackFrame when it
* beginsIonActivation (see ScriptFrameIter::interpFrame comment).
* safe since the engine avoids any observation of a StackFrame when it's
* runningInJit (see ScriptFrameIter::interpFrame comment).
*/
if (i.isIonOptimizedJS())
if (i.isIon())
continue;
AbstractFramePtr frame = i.abstractFramePtr();
if (frame.isFunctionFrame() && frame.script() == script) {

View File

@ -28,7 +28,7 @@ using namespace js::gc;
static void
CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned totalArgs)
{
JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon());
JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs);
@ -138,7 +138,7 @@ struct CopyScriptFrameIterArgs
{ }
void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const {
if (!iter_.isIon()) {
if (!iter_.isJit()) {
CopyStackFrameArguments(iter_.abstractFramePtr(), dstBase, totalArgs);
return;
}
@ -165,7 +165,7 @@ struct CopyScriptFrameIterArgs
* invalid.
*/
void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
if (!iter_.isIon())
if (!iter_.isJit())
ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
}
};

View File

@ -2029,20 +2029,17 @@ Debugger::getNewestFrame(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
/*
* cx->fp() would return the topmost frame in the current context.
* Since there may be multiple contexts, use AllFramesIter instead.
*/
for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
/* Since there may be multiple contexts, use AllFramesIter. */
for (AllFramesIter i(cx); !i.done(); ++i) {
/*
* Debug-mode currently disables Ion compilation in the compartment of
* the debuggee.
*/
if (i.isIonOptimizedJS())
if (i.isIon())
continue;
if (dbg->observesFrame(i.abstractFramePtr())) {
ScriptFrameIter iter(i.seg()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != i.abstractFramePtr())
ScriptFrameIter iter(i.activation()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
while (iter.isIon() || iter.abstractFramePtr() != i.abstractFramePtr())
++iter;
return dbg->getScriptFrame(cx, iter, args.rval());
}
@ -3855,7 +3852,7 @@ DebuggerFrame_getOlder(JSContext *cx, unsigned argc, Value *vp)
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
for (++iter; !iter.done(); ++iter) {
if (iter.isIonOptimizedJS())
if (iter.isIon())
continue;
if (dbg->observesFrame(iter.abstractFramePtr()))
return dbg->getScriptFrame(cx, iter, args.rval());

View File

@ -936,6 +936,10 @@ BailoutExplanation(ParallelBailoutCause cause)
return "unsupported string comparison";
case ParallelBailoutUnsupportedSparseArray:
return "unsupported sparse array";
case ParallelBailoutRequestedGC:
return "requested GC";
case ParallelBailoutRequestedZoneGC:
return "requested zone GC";
default:
return "no known reason";
}
@ -1445,6 +1449,8 @@ ForkJoinShared::check(ForkJoinSlice &slice)
// AutoRendezvous autoRendezvous(slice);
// if (!js_HandleExecutionInterrupt(cx_))
// return setAbortFlag(true);
slice.bailoutRecord->setCause(ParallelBailoutInterrupt,
NULL, NULL, NULL);
setAbortFlag(false);
return false;
}
@ -1641,6 +1647,8 @@ void
ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
{
shared->requestGC(reason);
bailoutRecord->setCause(ParallelBailoutRequestedGC,
NULL, NULL, NULL);
shared->setAbortFlag(false);
}
@ -1648,6 +1656,8 @@ void
ForkJoinSlice::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
{
shared->requestZoneGC(zone, reason);
bailoutRecord->setCause(ParallelBailoutRequestedZoneGC,
NULL, NULL, NULL);
shared->setAbortFlag(false);
}

View File

@ -258,6 +258,8 @@ enum ParallelBailoutCause {
ParallelBailoutUnsupported,
ParallelBailoutUnsupportedStringComparison,
ParallelBailoutUnsupportedSparseArray,
ParallelBailoutRequestedGC,
ParallelBailoutRequestedZoneGC,
};
struct ParallelBailoutTrace {

View File

@ -77,7 +77,7 @@ ComputeImplicitThis(JSContext *cx, HandleObject obj, MutableHandleValue vp)
inline bool
ComputeThis(JSContext *cx, AbstractFramePtr frame)
{
JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon());
JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInJit());
if (frame.thisValue().isObject())
return true;
RootedValue thisv(cx, frame.thisValue());
@ -732,7 +732,7 @@ GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObje
{
do {
// Don't call GetPcScript (needed for analysis) from inside Ion since it's expensive.
bool analyze = !cx->fp()->beginsIonActivation();
bool analyze = cx->mainThread().currentlyRunningInInterpreter();
uint32_t index;
if (IsDefinitelyIndex(rref, &index)) {
@ -893,7 +893,7 @@ SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, con
// that's ok, because optimized ion doesn't generate analysis info. However,
// baseline must generate this information, so it passes the script and pc in
// as arguments.
if (script || !cx->fp()->beginsIonActivation()) {
if (script || cx->mainThread().currentlyRunningInInterpreter()) {
JS_ASSERT(!!script == !!pc);
if (!script)
types::TypeScript::GetPcScript(cx, script.address(), &pc);

View File

@ -297,7 +297,6 @@ js::RunScript(JSContext *cx, StackFrame *fp)
{
ScriptFrameIter iter(cx);
if (!iter.done()) {
++iter;
JSScript *script = iter.script();
jsbytecode *pc = iter.pc();
if (UseNewType(cx, script, pc))
@ -1108,6 +1107,8 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode, bool
if (!entryFrame)
entryFrame = regs.fp();
InterpreterActivation activation(cx, entryFrame, regs);
#if JS_HAS_GENERATORS
if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
@ -1433,7 +1434,7 @@ BEGIN_CASE(JSOP_STOP)
jit_return:
#endif
/* The results of lowered call/apply frames need to be shifted. */
activation.popFrame(regs.fp());
cx->stack.popInlineFrame(regs);
SET_SCRIPT(regs.fp()->script());
@ -2226,6 +2227,8 @@ BEGIN_CASE(JSOP_FUNCALL)
if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial))
goto error;
activation.pushFrame(regs.fp());
if (newType)
regs.fp()->setUseNewType();
@ -3223,7 +3226,7 @@ js::Lambda(JSContext *cx, HandleFunction fun, HandleObject parent)
// emit code for a bound arrow function (bug 851913).
AbstractFramePtr frame = cx->fp();
#ifdef JS_ION
if (cx->fp()->runningInIon())
if (cx->mainThread().currentlyRunningInJit())
frame = ion::GetTopBaselineFrame(cx);
#endif
@ -3261,7 +3264,7 @@ js::DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain,
return false;
} else {
JS_ASSERT(script->compileAndGo);
JS_ASSERT_IF(!cx->fp()->beginsIonActivation(),
JS_ASSERT_IF(cx->mainThread().currentlyRunningInInterpreter(),
cx->fp()->isGlobalFrame() || cx->fp()->isEvalInFunction());
}

View File

@ -1079,7 +1079,7 @@ ScopeIter::settle()
type_ = Call;
hasScopeObject_ = false;
}
} else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx->runtime())) {
} else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) {
if (block_) {
JS_ASSERT(!block_->needsClone());
type_ = Block;
@ -1091,7 +1091,7 @@ ScopeIter::settle()
JS_ASSERT(cur_ == frame_.fun()->environment());
frame_ = NullFramePtr();
} else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx->runtime()));
JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx));
frame_ = NullFramePtr();
} else if (cur_->isWith()) {
JS_ASSERT_IF(frame_.isFunctionFrame(), frame_.fun()->isHeavyweight());
@ -1956,12 +1956,12 @@ DebugScopes::updateLiveScopes(JSContext *cx)
* to date' bit for fp->prev() in fp, simply popping fp effectively clears
* the flag for us, at exactly the time when execution resumes fp->prev().
*/
for (AllFramesIter i(cx->runtime()); !i.done(); ++i) {
for (AllFramesIter i(cx); !i.done(); ++i) {
/*
* Debug-mode currently disables Ion compilation in the compartment of
* the debuggee.
*/
if (i.isIonOptimizedJS())
if (i.isIon())
continue;
AbstractFramePtr frame = i.abstractFramePtr();

View File

@ -335,14 +335,17 @@ ContextStack::currentScript(jsbytecode **ppc,
if (ppc)
*ppc = NULL;
if (!hasfp())
Activation *act = cx_->mainThread().activation();
while (act && act->cx() != cx_)
act = act->prev();
if (!act)
return NULL;
FrameRegs &regs = this->regs();
StackFrame *fp = regs.fp();
JS_ASSERT(act->cx() == cx_);
#ifdef JS_ION
if (fp->beginsIonActivation()) {
if (act->isJit()) {
JSScript *script = NULL;
ion::GetPcScript(cx_, &script, ppc);
if (!allowCrossCompartment && script->compartment() != cx_->compartment())
@ -351,6 +354,11 @@ ContextStack::currentScript(jsbytecode **ppc,
}
#endif
JS_ASSERT(act->isInterpreter());
StackFrame *fp = act->asInterpreter()->current();
JS_ASSERT(!fp->runningInJit());
JSScript *script = fp->script();
if (!allowCrossCompartment && script->compartment() != cx_->compartment())
return NULL;
@ -370,7 +378,7 @@ template <class Op>
inline void
ScriptFrameIter::ionForEachCanonicalActualArg(JSContext *cx, Op op)
{
JS_ASSERT(isIon());
JS_ASSERT(isJit());
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS()) {
ionInlineFrames_.forEachCanonicalActualArg(cx, op, 0, -1);
@ -868,5 +876,32 @@ AbstractFramePtr::popWith(JSContext *cx) const
JS_NOT_REACHED("Invalid frame");
}
Activation::Activation(JSContext *cx, Kind kind)
: cx_(cx),
compartment_(cx->compartment()),
prev_(cx->mainThread().activation_),
savedFrameChain_(0),
kind_(kind)
{
cx->mainThread().activation_ = this;
}
Activation::~Activation()
{
JS_ASSERT(cx_->mainThread().activation_ == this);
cx_->mainThread().activation_ = prev_;
}
InterpreterActivation::InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs)
: Activation(cx, Interpreter),
entry_(entry),
current_(entry),
regs_(regs)
{}
// Define destructor explicitly to silence GCC used-but-never-defined warning.
InterpreterActivation::~InterpreterActivation()
{}
} /* namespace js */
#endif /* Stack_inl_h__ */

View File

@ -54,13 +54,6 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFra
FrameRegs *regs, const Value &thisv, JSObject &scopeChain,
ExecuteType type)
{
/*
* If |prev| is an interpreter frame, we can always prev-link to it.
* If |prev| is a baseline JIT frame, we prev-link to its entry frame.
*/
JS_ASSERT_IF(prev.isStackFrame(), prev.asStackFrame() == prevLink);
JS_ASSERT_IF(prev, prevLink != NULL);
/*
* See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
* script in the context of another frame and the frame type is determined
@ -93,11 +86,9 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prevLink, AbstractFra
prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
blockChain_ = NULL;
#ifdef JS_ION
/* Set prevBaselineFrame_ if this is an eval frame. */
/* Set evalInFramePrev_ if this is an eval-in-frame. */
JS_ASSERT_IF(isDebuggerFrame(), isEvalFrame());
prevBaselineFrame_ = (isEvalFrame() && prev.isBaselineFrame()) ? prev.asBaselineFrame() : NULL;
#endif
evalInFramePrev_ = isDebuggerFrame() ? prev : (StackFrame *)NULL;
#ifdef DEBUG
Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
@ -771,12 +762,12 @@ StackSpace::containsSlow(StackFrame *fp)
{
if (!seg_)
return false;
for (AllFramesIter i(seg_->cx()->runtime()); !i.done(); ++i) {
for (AllFramesIter i(seg_->cx()); !i.done(); ++i) {
/*
* Debug-mode currently disables Ion compilation in the compartment of
* the debuggee.
*/
if (i.isIon())
if (i.isJit())
continue;
if (i.interpFrame() == fp)
return true;
@ -944,24 +935,8 @@ ContextStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &
StackFrame *prevLink;
AbstractFramePtr prev = NullFramePtr();
if (evalInFrame) {
/* First, find the right segment. */
AllFramesIter frameIter(cx->runtime());
while (frameIter.isIonOptimizedJS() || frameIter.abstractFramePtr() != evalInFrame)
++frameIter;
JS_ASSERT(frameIter.abstractFramePtr() == evalInFrame);
StackSegment &seg = *frameIter.seg();
ScriptFrameIter iter(cx->runtime(), seg);
/* Debug-mode currently disables Ion compilation. */
JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInIon());
JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != evalInFrame) {
++iter;
JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
}
JS_ASSERT(iter.abstractFramePtr() == evalInFrame);
prevLink = iter.data_.fp_;
JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInJit());
prevLink = NULL;
prev = evalInFrame;
extend = CANT_EXTEND;
} else {
@ -969,7 +944,7 @@ ContextStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &
extend = CAN_EXTEND;
if (maybefp()) {
ScriptFrameIter iter(cx);
prev = iter.isIonOptimizedJS() ? maybefp() : iter.abstractFramePtr();
prev = iter.isIon() ? maybefp() : iter.abstractFramePtr();
}
}
@ -1101,164 +1076,105 @@ ContextStack::restoreFrameChain()
/*****************************************************************************/
void
ScriptFrameIter::poisonRegs()
ScriptFrameIter::popActivation()
{
data_.pc_ = (jsbytecode *)0xbad;
++data_.activations_;
settleOnActivation();
}
void
ScriptFrameIter::popFrame()
ScriptFrameIter::popInterpreterFrame()
{
StackFrame *oldfp = data_.fp_;
JS_ASSERT(data_.seg_->contains(oldfp));
data_.fp_ = data_.fp_->prev();
JS_ASSERT(data_.state_ == SCRIPTED);
if (data_.seg_->contains(data_.fp_))
data_.pc_ = oldfp->prevpc();
++data_.interpFrames_;
if (data_.interpFrames_.done())
popActivation();
else
poisonRegs();
data_.pc_ = data_.interpFrames_.pc();
}
void
ScriptFrameIter::settleOnNewSegment()
ScriptFrameIter::settleOnActivation()
{
if (FrameRegs *regs = data_.seg_->maybeRegs())
data_.pc_ = regs->pc;
else
poisonRegs();
}
void
ScriptFrameIter::startOnSegment(StackSegment *seg)
{
data_.seg_ = seg;
data_.fp_ = data_.seg_->maybefp();
settleOnNewSegment();
}
/*
* Given the iterator's current value of fp_ (initialized on construction or
* after operator++ popped the previous call), "settle" the iterator on a new
* ScriptFrameIter::State value. The goal is to present the client a simple
* linear sequence of scripted calls while covering up unpleasant stack
* implementation details:
* - The frame chain can be "saved" and "restored" (see JS_SaveFrameChain).
* This artificially cuts the call chain and the ScriptFrameIter client may
* want to continue through this cut to the previous frame by passing
* GO_THROUGH_SAVED.
* - fp->prev can be in a different contiguous segment from fp. In this case,
* the current values of sp/pc after calling popFrame are incorrect and
* should be recovered from fp->prev's segment.
*/
/* PGO causes xpcshell startup crashes with VS2010. */
#if defined(_MSC_VER)
# pragma optimize("g", off)
#endif
void
ScriptFrameIter::settleOnNewState()
{
/*
* There are elements of the fp_ chain that we want to skip over so iterate
* until we settle on one or until there are no more.
*/
while (true) {
if (!data_.fp_) {
if (data_.savedOption_ == GO_THROUGH_SAVED && data_.seg_->prevInContext()) {
startOnSegment(data_.seg_->prevInContext());
continue;
}
if (data_.activations_.done()) {
data_.state_ = DONE;
return;
}
/* Check if popFrame changed segment. */
bool containsFrame = data_.seg_->contains(data_.fp_);
while (!containsFrame) {
/* Eval-in-frame can cross contexts, so use prevInMemory. */
data_.seg_ = data_.seg_->prevInMemory();
containsFrame = data_.seg_->contains(data_.fp_);
Activation *activation = data_.activations_.activation();
/* Eval-in-frame allows jumping into the middle of a segment. */
if (containsFrame && data_.seg_->fp() != data_.fp_) {
/* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
ScriptFrameIter tmp = *this;
tmp.startOnSegment(data_.seg_);
tmp.settleOnNewState();
while (tmp.data_.fp_ != data_.fp_)
++tmp;
JS_ASSERT(!tmp.done() &&
tmp.data_.seg_ == data_.seg_ &&
tmp.data_.fp_ == data_.fp_);
*this = tmp;
return;
}
// If JS_SaveFrameChain was called, stop iterating here (unless
// GO_THROUGH_SAVED is set).
if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) {
data_.state_ = DONE;
return;
}
settleOnNewSegment();
// Skip activations from another context if needed.
JS_ASSERT(activation->cx());
JS_ASSERT(data_.cx_);
if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) {
++data_.activations_;
continue;
}
#ifdef JS_ION
if (data_.fp_->beginsIonActivation()) {
/*
* Eval-in-frame can link to an arbitrary frame on the stack.
* Skip any IonActivation's until we reach the one for the
* current StackFrame. Treat activations with NULL entryfp
* (pushed by FastInvoke) as belonging to the previous
* activation.
*/
while (true) {
ion::IonActivation *act = data_.ionActivations_.activation();
while (!act->entryfp())
act = act->prev();
if (act->entryfp() == data_.fp_)
break;
++data_.ionActivations_;
}
data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
if (activation->isJit()) {
data_.ionFrames_ = ion::IonFrameIterator(data_.activations_);
// Stop at the first scripted frame.
while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
++data_.ionFrames_;
// When invoked from JM, we don't re-use the entryfp, so we
// may have an empty Ion activation.
// It's possible to have an JitActivation with no scripted frames,
// for instance if we hit an over-recursion during bailout.
if (data_.ionFrames_.done()) {
data_.state_ = SCRIPTED;
return;
++data_.activations_;
continue;
}
data_.state_ = ION;
nextIonFrame();
nextJitFrame();
data_.state_ = JIT;
return;
}
#endif /* JS_ION */
#endif
JS_ASSERT(activation->isInterpreter());
InterpreterActivation *interpAct = activation->asInterpreter();
data_.interpFrames_ = InterpreterFrameIterator(interpAct);
// If we OSR'ed into JIT code, skip the interpreter frame so that
// the same frame is not reported twice.
if (data_.interpFrames_.frame()->runningInJit()) {
++data_.interpFrames_;
if (data_.interpFrames_.done()) {
++data_.activations_;
continue;
}
}
JS_ASSERT(!data_.interpFrames_.frame()->runningInJit());
data_.pc_ = data_.interpFrames_.pc();
data_.state_ = SCRIPTED;
return;
}
}
#if defined(_MSC_VER)
# pragma optimize("", on)
#endif
ScriptFrameIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption)
ScriptFrameIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption,
ContextOption contextOption)
: perThread_(perThread),
cx_(cx),
savedOption_(savedOption)
savedOption_(savedOption),
contextOption_(contextOption),
pc_(NULL),
interpFrames_(NULL),
activations_(cx->runtime())
#ifdef JS_ION
, ionActivations_(cx),
ionFrames_((uint8_t *)NULL)
#endif
{
}
ScriptFrameIter::Data::Data(JSContext *cx, JSRuntime *rt, StackSegment *seg)
: perThread_(&rt->mainThread),
cx_(cx),
savedOption_(STOP_AT_SAVED)
#ifdef JS_ION
, ionActivations_(rt),
ionFrames_((uint8_t *)NULL)
, ionFrames_((uint8_t *)NULL)
#endif
{
}
@ -1267,45 +1183,39 @@ ScriptFrameIter::Data::Data(const ScriptFrameIter::Data &other)
: perThread_(other.perThread_),
cx_(other.cx_),
savedOption_(other.savedOption_),
contextOption_(other.contextOption_),
state_(other.state_),
fp_(other.fp_),
seg_(other.seg_),
pc_(other.pc_)
pc_(other.pc_),
interpFrames_(other.interpFrames_),
activations_(other.activations_)
#ifdef JS_ION
, ionActivations_(other.ionActivations_),
ionFrames_(other.ionFrames_)
, ionFrames_(other.ionFrames_)
#endif
{
}
ScriptFrameIter::ScriptFrameIter(JSContext *cx, SavedOption savedOption)
: data_(cx, &cx->runtime()->mainThread, savedOption)
: data_(cx, &cx->runtime()->mainThread, savedOption, CURRENT_CONTEXT)
#ifdef JS_ION
, ionInlineFrames_(cx, (js::ion::IonFrameIterator*) NULL)
#endif
{
if (StackSegment *seg = cx->stack.seg_) {
startOnSegment(seg);
settleOnNewState();
} else {
data_.state_ = DONE;
}
settleOnActivation();
}
ScriptFrameIter::ScriptFrameIter(JSRuntime *rt, StackSegment &seg)
: data_(seg.cx(), rt, &seg)
ScriptFrameIter::ScriptFrameIter(JSContext *cx, ContextOption contextOption, SavedOption savedOption)
: data_(cx, &cx->runtime()->mainThread, savedOption, contextOption)
#ifdef JS_ION
, ionInlineFrames_(seg.cx(), (js::ion::IonFrameIterator*) NULL)
, ionInlineFrames_(cx, (js::ion::IonFrameIterator*) NULL)
#endif
{
startOnSegment(&seg);
settleOnNewState();
settleOnActivation();
}
ScriptFrameIter::ScriptFrameIter(const ScriptFrameIter &other)
: data_(other.data_)
#ifdef JS_ION
, ionInlineFrames_(other.data_.seg_->cx(),
, ionInlineFrames_(other.data_.cx_,
data_.ionFrames_.isScripted() ? &other.ionInlineFrames_ : NULL)
#endif
{
@ -1322,7 +1232,7 @@ ScriptFrameIter::ScriptFrameIter(const Data &data)
#ifdef JS_ION
void
ScriptFrameIter::nextIonFrame()
ScriptFrameIter::nextJitFrame()
{
if (data_.ionFrames_.isOptimizedJS()) {
ionInlineFrames_.resetOn(&data_.ionFrames_);
@ -1334,58 +1244,26 @@ ScriptFrameIter::nextIonFrame()
}
void
ScriptFrameIter::popIonFrame()
ScriptFrameIter::popJitFrame()
{
// Keep fp which describes all ion frames.
poisonRegs();
JS_ASSERT(data_.state_ == JIT);
if (data_.ionFrames_.isOptimizedJS() && ionInlineFrames_.more()) {
++ionInlineFrames_;
data_.pc_ = ionInlineFrames_.pc();
} else {
++data_.ionFrames_;
while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
++data_.ionFrames_;
if (!data_.ionFrames_.done()) {
nextIonFrame();
return;
}
// The activation has no other frames. If entryfp is NULL, it was invoked
// by a native written in C++, using FastInvoke, on top of another activation.
ion::IonActivation *activation = data_.ionActivations_.activation();
if (!activation->entryfp()) {
JS_ASSERT(activation->prevpc());
JS_ASSERT(data_.fp_->beginsIonActivation());
++data_.ionActivations_;
settleOnNewState();
return;
}
if (data_.fp_->runningInIon()) {
++data_.ionActivations_;
popFrame();
settleOnNewState();
} else {
JS_ASSERT(data_.fp_->callingIntoIon());
data_.state_ = SCRIPTED;
data_.pc_ = data_.ionActivations_.activation()->prevpc();
++data_.ionActivations_;
}
return;
}
}
void
ScriptFrameIter::popBaselineDebuggerFrame()
{
ion::BaselineFrame *prevBaseline = data_.fp_->prevBaselineFrame();
++data_.ionFrames_;
while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
++data_.ionFrames_;
popFrame();
settleOnNewState();
if (!data_.ionFrames_.done()) {
nextJitFrame();
return;
}
JS_ASSERT(data_.state_ == ION);
while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != prevBaseline)
popIonFrame();
popActivation();
}
#endif
@ -1396,19 +1274,41 @@ ScriptFrameIter::operator++()
case DONE:
JS_NOT_REACHED("Unexpected state");
case SCRIPTED:
if (interpFrame()->isDebuggerFrame()) {
AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
JS_ASSERT(eifPrev);
// Eval-in-frame can cross contexts and works across saved frame
// chains.
ContextOption prevContextOption = data_.contextOption_;
SavedOption prevSavedOption = data_.savedOption_;
data_.contextOption_ = ALL_CONTEXTS;
data_.savedOption_ = GO_THROUGH_SAVED;
popInterpreterFrame();
while (isIon() || abstractFramePtr() != eifPrev) {
if (data_.state_ == JIT) {
#ifdef JS_ION
if (data_.fp_->isDebuggerFrame() && data_.fp_->prevBaselineFrame()) {
/* Eval-in-frame with a baseline JIT frame. */
popBaselineDebuggerFrame();
popJitFrame();
#else
JS_NOT_REACHED("Invalid state");
#endif
} else {
popInterpreterFrame();
}
}
data_.contextOption_ = prevContextOption;
data_.savedOption_ = prevSavedOption;
data_.cx_ = data_.activations_.activation()->cx();
break;
}
#endif
popFrame();
settleOnNewState();
popInterpreterFrame();
break;
case ION:
case JIT:
#ifdef JS_ION
popIonFrame();
popJitFrame();
break;
#else
JS_NOT_REACHED("Unexpected state");
@ -1417,13 +1317,6 @@ ScriptFrameIter::operator++()
return *this;
}
bool
ScriptFrameIter::operator==(const ScriptFrameIter &rhs) const
{
return done() == rhs.done() &&
(done() || data_.fp_ == rhs.data_.fp_);
}
ScriptFrameIter::Data *
ScriptFrameIter::copyData() const
{
@ -1444,13 +1337,8 @@ ScriptFrameIter::compartment() const
case DONE:
break;
case SCRIPTED:
return data_.fp_->compartment();
case ION:
#ifdef JS_ION
return data_.ionActivations_.activation()->compartment();
#else
break;
#endif
case JIT:
return data_.activations_.activation()->compartment();
}
JS_NOT_REACHED("Unexpected state");
return NULL;
@ -1464,7 +1352,7 @@ ScriptFrameIter::isFunctionFrame() const
break;
case SCRIPTED:
return interpFrame()->isFunctionFrame();
case ION:
case JIT:
#ifdef JS_ION
JS_ASSERT(data_.ionFrames_.isScripted());
if (data_.ionFrames_.isBaselineJS())
@ -1486,7 +1374,7 @@ ScriptFrameIter::isGlobalFrame() const
break;
case SCRIPTED:
return interpFrame()->isGlobalFrame();
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS())
return data_.ionFrames_.baselineFrame()->isGlobalFrame();
@ -1508,7 +1396,7 @@ ScriptFrameIter::isEvalFrame() const
break;
case SCRIPTED:
return interpFrame()->isEvalFrame();
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS())
return data_.ionFrames_.baselineFrame()->isEvalFrame();
@ -1531,7 +1419,7 @@ ScriptFrameIter::isNonEvalFunctionFrame() const
break;
case SCRIPTED:
return interpFrame()->isNonEvalFunctionFrame();
case ION:
case JIT:
return !isEvalFrame() && isFunctionFrame();
}
JS_NOT_REACHED("Unexpected state");
@ -1546,7 +1434,7 @@ ScriptFrameIter::isGeneratorFrame() const
break;
case SCRIPTED:
return interpFrame()->isGeneratorFrame();
case ION:
case JIT:
return false;
}
JS_NOT_REACHED("Unexpected state");
@ -1559,7 +1447,7 @@ ScriptFrameIter::isConstructing() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS())
return ionInlineFrames_.isConstructing();
@ -1581,7 +1469,7 @@ ScriptFrameIter::abstractFramePtr() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS())
return data_.ionFrames_.baselineFrame();
@ -1604,20 +1492,20 @@ ScriptFrameIter::updatePcQuadratic()
case SCRIPTED:
data_.pc_ = interpFrame()->pcQuadratic(data_.cx_);
return;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS()) {
ion::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
ion::IonActivation *activation = data_.ionActivations_.activation();
ion::JitActivation *activation = data_.activations_.activation()->asJit();
// IonActivationIterator::top may be invalid, so create a new
// ActivationIterator::ionTop_ may be invalid, so create a new
// activation iterator.
data_.ionActivations_ = ion::IonActivationIterator(data_.cx_);
while (data_.ionActivations_.activation() != activation)
++data_.ionActivations_;
data_.activations_ = ActivationIterator(data_.cx_->runtime());
while (data_.activations_.activation() != activation)
++data_.activations_;
// Look for the current frame.
data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
data_.ionFrames_ = ion::IonFrameIterator(data_.activations_);
while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != frame)
++data_.ionFrames_;
@ -1641,7 +1529,7 @@ ScriptFrameIter::callee() const
case SCRIPTED:
JS_ASSERT(isFunctionFrame());
return &interpFrame()->callee();
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS())
return data_.ionFrames_.callee();
@ -1664,7 +1552,7 @@ ScriptFrameIter::calleev() const
case SCRIPTED:
JS_ASSERT(isFunctionFrame());
return interpFrame()->calleev();
case ION:
case JIT:
#ifdef JS_ION
return ObjectValue(*callee());
#else
@ -1684,7 +1572,7 @@ ScriptFrameIter::numActualArgs() const
case SCRIPTED:
JS_ASSERT(isFunctionFrame());
return interpFrame()->numActualArgs();
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS())
return ionInlineFrames_.numActualArgs();
@ -1707,7 +1595,7 @@ ScriptFrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) c
break;
case SCRIPTED:
return interpFrame()->unaliasedActual(i, checkAliasing);
case ION:
case JIT:
#ifdef JS_ION
JS_ASSERT(data_.ionFrames_.isBaselineJS());
return data_.ionFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
@ -1725,7 +1613,7 @@ ScriptFrameIter::scopeChain() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS())
return ionInlineFrames_.scopeChain();
@ -1759,7 +1647,7 @@ ScriptFrameIter::hasArgsObj() const
break;
case SCRIPTED:
return interpFrame()->hasArgsObj();
case ION:
case JIT:
#ifdef JS_ION
JS_ASSERT(data_.ionFrames_.isBaselineJS());
return data_.ionFrames_.baselineFrame()->hasArgsObj();
@ -1779,7 +1667,7 @@ ScriptFrameIter::argsObj() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
JS_ASSERT(data_.ionFrames_.isBaselineJS());
return data_.ionFrames_.baselineFrame()->argsObj();
@ -1797,7 +1685,7 @@ bool
ScriptFrameIter::computeThis() const
{
JS_ASSERT(!done());
if (!isIonOptimizedJS()) {
if (!isIon()) {
JS_ASSERT(data_.cx_);
return ComputeThis(data_.cx_, abstractFramePtr());
}
@ -1810,7 +1698,7 @@ ScriptFrameIter::thisv() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS())
return ObjectValue(*ionInlineFrames_.thisObject());
@ -1831,7 +1719,7 @@ ScriptFrameIter::returnValue() const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS())
return *data_.ionFrames_.baselineFrame()->returnValue();
@ -1850,7 +1738,7 @@ ScriptFrameIter::setReturnValue(const Value &v)
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isBaselineJS()) {
data_.ionFrames_.baselineFrame()->setReturnValue(v);
@ -1871,7 +1759,7 @@ ScriptFrameIter::numFrameSlots() const
switch (data_.state_) {
case DONE:
break;
case ION: {
case JIT: {
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS())
return ionInlineFrames_.snapshotIterator().slots() - ionInlineFrames_.script()->nfixed;
@ -1896,7 +1784,7 @@ ScriptFrameIter::frameSlotValue(size_t index) const
switch (data_.state_) {
case DONE:
break;
case ION:
case JIT:
#ifdef JS_ION
if (data_.ionFrames_.isOptimizedJS()) {
ion::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
@ -1918,129 +1806,12 @@ ScriptFrameIter::frameSlotValue(size_t index) const
/*****************************************************************************/
AllFramesIter::AllFramesIter(JSRuntime *rt)
: seg_(rt->stackSpace.seg_),
fp_(seg_ ? seg_->maybefp() : NULL)
#ifdef JS_ION
, ionActivations_(rt),
ionFrames_((uint8_t *)NULL)
#endif
{
settleOnNewState();
}
#ifdef JS_ION
void
AllFramesIter::popIonFrame()
{
JS_ASSERT(state_ == ION);
++ionFrames_;
while (!ionFrames_.done() && !ionFrames_.isScripted())
++ionFrames_;
if (!ionFrames_.done())
return;
// The activation has no other frames. If entryfp is NULL, it was invoked
// by a native written in C++, using FastInvoke, on top of another activation.
ion::IonActivation *activation = ionActivations_.activation();
if (!activation->entryfp()) {
JS_ASSERT(activation->prevpc());
JS_ASSERT(fp_->beginsIonActivation());
++ionActivations_;
settleOnNewState();
return;
}
if (fp_->runningInIon()) {
++ionActivations_;
fp_ = fp_->prev();
settleOnNewState();
} else {
JS_ASSERT(fp_->callingIntoIon());
state_ = SCRIPTED;
++ionActivations_;
}
}
#endif
AllFramesIter&
AllFramesIter::operator++()
{
switch (state_) {
case SCRIPTED:
fp_ = fp_->prev();
settleOnNewState();
break;
#ifdef JS_ION
case ION:
popIonFrame();
break;
#endif
case DONE:
default:
JS_NOT_REACHED("Unexpeced state");
}
return *this;
}
void
AllFramesIter::settleOnNewState()
{
while (seg_ && (!fp_ || !seg_->contains(fp_))) {
seg_ = seg_->prevInMemory();
fp_ = seg_ ? seg_->maybefp() : NULL;
}
JS_ASSERT(!!seg_ == !!fp_);
JS_ASSERT_IF(fp_, seg_->contains(fp_));
#ifdef JS_ION
if (fp_ && fp_->beginsIonActivation()) {
// Start at the first scripted frame.
ionFrames_ = ion::IonFrameIterator(ionActivations_);
while (!ionFrames_.isScripted() && !ionFrames_.done())
++ionFrames_;
state_ = ionFrames_.done() ? SCRIPTED : ION;
return;
}
#endif
state_ = fp_ ? SCRIPTED : DONE;
}
AbstractFramePtr
AllFramesIter::abstractFramePtr() const
{
switch (state_) {
case SCRIPTED:
return AbstractFramePtr(interpFrame());
case ION:
#ifdef JS_ION
if (ionFrames_.isBaselineJS())
return ionFrames_.baselineFrame();
#endif
break;
case DONE:
break;
}
JS_NOT_REACHED("Unexpected state");
return NullFramePtr();
}
JSObject *
AbstractFramePtr::evalPrevScopeChain(JSRuntime *rt) const
AbstractFramePtr::evalPrevScopeChain(JSContext *cx) const
{
/* Find the stack segment containing this frame. */
AllFramesIter alliter(rt);
while (alliter.isIonOptimizedJS() || alliter.abstractFramePtr() != *this)
++alliter;
/* Eval frames are not compiled by Ion, though their caller might be. */
ScriptFrameIter iter(rt, *alliter.seg());
while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != *this)
// Eval frames are not compiled by Ion, though their caller might be.
AllFramesIter iter(cx);
while (iter.isIon() || iter.abstractFramePtr() != *this)
++iter;
++iter;
return iter.scopeChain();
@ -2068,3 +1839,42 @@ js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
}
}
#endif
ion::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing)
: Activation(cx, Jit),
prevIonTop_(cx->mainThread().ionTop),
prevIonJSContext_(cx->mainThread().ionJSContext),
firstFrameIsConstructing_(firstFrameIsConstructing)
{
cx->mainThread().ionJSContext = cx;
}
ion::JitActivation::~JitActivation()
{
cx_->mainThread().ionTop = prevIonTop_;
cx_->mainThread().ionJSContext = prevIonJSContext_;
}
InterpreterFrameIterator &
InterpreterFrameIterator::operator++()
{
JS_ASSERT(!done());
pc_ = fp_->prevpc();
fp_ = (fp_ != activation_->entry_) ? fp_->prev() : NULL;
return *this;
}
ActivationIterator::ActivationIterator(JSRuntime *rt)
: jitTop_(rt->mainThread.ionTop),
activation_(rt->mainThread.activation_)
{ }
ActivationIterator &
ActivationIterator::operator++()
{
JS_ASSERT(activation_);
if (activation_->isJit())
jitTop_ = activation_->asJit()->prevIonTop();
activation_ = activation_->prev();
return *this;
}

View File

@ -88,9 +88,8 @@ struct ScopeCoordinate;
* segment's "current regs", which contains the stack pointer 'sp'. In the
* interpreter, sp is adjusted as individual values are pushed and popped from
* the stack and the FrameRegs struct (pointed by the StackSegment) is a local
* var of js::Interpret. JM JIT code simulates this by lazily updating FrameRegs
* when calling from JIT code into the VM. Ideally, we'd like to remove all
* dependence on FrameRegs outside the interpreter.
* var of js::Interpret. Ideally, we'd like to remove all dependence on FrameRegs
* outside the interpreter.
*
* An additional feature (perhaps not for much longer: bug 650361) is that
* multiple independent "contexts" can interleave (LIFO) on a single contiguous
@ -104,10 +103,10 @@ struct ScopeCoordinate;
* to cx2 causes a new segment to be started for cx2's stack on top of cx1's
* current segment. These two segments are linked from the perspective of
* StackSpace, since they are adjacent on the thread's stack, but not from the
* perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
* and prevInContext. Each independent stack is encapsulated and managed by
* the js::ContextStack object stored in JSContext. ContextStack is the primary
* interface to the rest of the engine for pushing and popping the stack.
* perspective of cx1 and cx2. Each independent stack is encapsulated and
* managed by the js::ContextStack object stored in JSContext. ContextStack
* is the primary interface to the rest of the engine for pushing and popping
* the stack.
*/
/*****************************************************************************/
@ -227,7 +226,7 @@ class AbstractFramePtr
inline bool prevUpToDate() const;
inline void setPrevUpToDate() const;
JSObject *evalPrevScopeChain(JSRuntime *rt) const;
JSObject *evalPrevScopeChain(JSContext *cx) const;
inline void *maybeHookData() const;
inline void setHookData(void *data) const;
@ -301,12 +300,14 @@ class StackFrame
/* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
HAS_PUSHED_SPS_FRAME = 0x10000, /* SPS was notified of enty */
/* Ion frame state */
RUNNING_IN_ION = 0x20000, /* frame is running in Ion */
CALLING_INTO_ION = 0x40000, /* frame is calling into Ion */
/*
* If set, we entered one of the JITs and ScriptFrameIter should skip
* this frame.
*/
RUNNING_IN_JIT = 0x20000,
/* Miscellaneous state. */
USE_NEW_TYPE = 0x80000 /* Use new type for constructed |this| object. */
USE_NEW_TYPE = 0x40000 /* Use new type for constructed |this| object. */
};
private:
@ -326,10 +327,7 @@ class StackFrame
ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */
jsbytecode *prevpc_; /* if HAS_PREVPC, pc of previous frame*/
void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */
#ifdef JS_ION
ion::BaselineFrame *prevBaselineFrame_; /* for an eval/debugger frame, the baseline frame
* to use as prev. */
#endif
AbstractFramePtr evalInFramePrev_; /* for an eval/debugger frame, the prev frame */
static void staticAsserts() {
JS_STATIC_ASSERT(offsetof(StackFrame, rval_) % sizeof(Value) == 0);
@ -477,18 +475,10 @@ class StackFrame
return prev_;
}
#ifdef JS_ION
/*
* To handle eval-in-frame with a baseline JIT frame, |prev_| points to the
* entry frame and prevBaselineFrame_ to the actual BaselineFrame. This is
* done so that ScriptFrameIter can skip JIT frames pushed on top of the
* baseline frame (these frames should not appear in stack traces).
*/
ion::BaselineFrame *prevBaselineFrame() const {
AbstractFramePtr evalInFramePrev() const {
JS_ASSERT(isEvalFrame());
return prevBaselineFrame_;
return evalInFramePrev_;
}
#endif
inline void resetGeneratorPrev(JSContext *cx);
@ -986,29 +976,15 @@ class StackFrame
public:
void mark(JSTracer *trc);
// Entered IonMonkey from the interpreter.
bool runningInIon() const {
return !!(flags_ & RUNNING_IN_ION);
// Entered Baseline/Ion from the interpreter.
bool runningInJit() const {
return !!(flags_ & RUNNING_IN_JIT);
}
// Entered IonMonkey from JaegerMonkey.
bool callingIntoIon() const {
return !!(flags_ & CALLING_INTO_ION);
void setRunningInJit() {
flags_ |= RUNNING_IN_JIT;
}
// Entered IonMonkey in any way.
bool beginsIonActivation() const {
return !!(flags_ & (RUNNING_IN_ION | CALLING_INTO_ION));
}
void setRunningInIon() {
flags_ |= RUNNING_IN_ION;
}
void setCallingIntoIon() {
flags_ |= CALLING_INTO_ION;
}
void clearRunningInIon() {
flags_ &= ~RUNNING_IN_ION;
}
void clearCallingIntoIon() {
flags_ &= ~CALLING_INTO_ION;
void clearRunningInJit() {
flags_ &= ~RUNNING_IN_JIT;
}
};
@ -1073,18 +1049,6 @@ class FrameRegs
JS_ASSERT(fp_);
}
/* For FixupArity: */
void popPartialFrame(Value *newsp) {
sp = newsp;
fp_ = fp_->prev();
JS_ASSERT(fp_);
}
/* For InternalInterpret: */
void restorePartialFrame(Value *newfp) {
fp_ = (StackFrame *) newfp;
}
/* For stubs::CompileFunction, ContextStack: */
void prepareToRun(StackFrame &fp, JSScript *script) {
pc = script->code;
@ -1528,6 +1492,225 @@ struct DefaultHasher<AbstractFramePtr> {
/*****************************************************************************/
class InterpreterActivation;
namespace ion {
class JitActivation;
};
// A JSRuntime's stack consists of a linked list of activations. Every activation
// contains a number of scripted frames that are either running in the interpreter
// (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
// activation are contiguous: whenever C++ calls back into JS, a new activation is
// pushed.
//
// Every activation is tied to a single JSContext and JSCompartment. This means we
// can construct a given context's stack by skipping activations belonging to other
// contexts.
class Activation
{
protected:
JSContext *cx_;
JSCompartment *compartment_;
Activation *prev_;
// Counter incremented by JS_SaveFrameChain on the top-most activation and
// decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop
// iterating when it reaches this activation (if GO_THROUGH_SAVED is not
// set).
size_t savedFrameChain_;
enum Kind { Interpreter, Jit };
Kind kind_;
inline Activation(JSContext *cx, Kind kind_);
inline ~Activation();
public:
JSContext *cx() const {
return cx_;
}
JSCompartment *compartment() const {
return compartment_;
}
Activation *prev() const {
return prev_;
}
bool isInterpreter() const {
return kind_ == Interpreter;
}
bool isJit() const {
return kind_ == Jit;
}
InterpreterActivation *asInterpreter() const {
JS_ASSERT(isInterpreter());
return (InterpreterActivation *)this;
}
ion::JitActivation *asJit() const {
JS_ASSERT(isJit());
return (ion::JitActivation *)this;
}
void saveFrameChain() {
savedFrameChain_++;
}
void restoreFrameChain() {
JS_ASSERT(savedFrameChain_ > 0);
savedFrameChain_--;
}
bool hasSavedFrameChain() const {
return savedFrameChain_ > 0;
}
private:
Activation(const Activation &other) MOZ_DELETE;
void operator=(const Activation &other) MOZ_DELETE;
};
class InterpreterFrameIterator;
class InterpreterActivation : public Activation
{
friend class js::InterpreterFrameIterator;
StackFrame *const entry_; // Entry frame for this activation.
StackFrame *current_; // The most recent frame.
FrameRegs &regs_;
public:
inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs &regs);
inline ~InterpreterActivation();
void pushFrame(StackFrame *frame) {
JS_ASSERT(frame->script()->compartment() == compartment_);
current_ = frame;
}
void popFrame(StackFrame *frame) {
JS_ASSERT(current_ == frame);
JS_ASSERT(current_ != entry_);
current_ = frame->prev();
JS_ASSERT(current_);
}
StackFrame *current() const {
JS_ASSERT(current_);
return current_;
}
};
// Iterates over a runtime's activation list.
class ActivationIterator
{
uint8_t *jitTop_;
protected:
Activation *activation_;
public:
explicit ActivationIterator(JSRuntime *rt);
ActivationIterator &operator++();
Activation *activation() const {
return activation_;
}
uint8_t *jitTop() const {
JS_ASSERT(activation_->isJit());
return jitTop_;
}
bool done() const {
return activation_ == NULL;
}
};
namespace ion {
// A JitActivation is used for frames running in Baseline or Ion.
class JitActivation : public Activation
{
uint8_t *prevIonTop_;
JSContext *prevIonJSContext_;
bool firstFrameIsConstructing_;
public:
JitActivation(JSContext *cx, bool firstFrameIsConstructing);
~JitActivation();
uint8_t *prevIonTop() const {
return prevIonTop_;
}
JSCompartment *compartment() const {
return compartment_;
}
bool firstFrameIsConstructing() const {
return firstFrameIsConstructing_;
}
};
// A filtering of the ActivationIterator to only stop at JitActivations.
class JitActivationIterator : public ActivationIterator
{
void settle() {
while (!done() && !activation_->isJit())
ActivationIterator::operator++();
}
public:
explicit JitActivationIterator(JSRuntime *rt)
: ActivationIterator(rt)
{
settle();
}
JitActivationIterator &operator++() {
ActivationIterator::operator++();
settle();
return *this;
}
// Returns the bottom and top addresses of the current JitActivation.
void jitStackRange(uintptr_t *&min, uintptr_t *&end);
};
} // namespace ion
// Iterates over the frames of a single InterpreterActivation.
class InterpreterFrameIterator
{
InterpreterActivation *activation_;
StackFrame *fp_;
jsbytecode *pc_;
public:
explicit InterpreterFrameIterator(InterpreterActivation *activation)
: activation_(activation),
fp_(NULL),
pc_(NULL)
{
if (activation) {
fp_ = activation->current();
pc_ = activation->regs_.pc;
}
}
StackFrame *frame() const {
JS_ASSERT(!done());
return fp_;
}
jsbytecode *pc() const {
JS_ASSERT(!done());
return pc_;
}
InterpreterFrameIterator &operator++();
bool done() const {
return fp_ == NULL;
}
};
/*
* Iterate through the callstack (following fp->prev) of the given context.
* Each element of said callstack can either be the execution of a script
@ -1550,7 +1733,8 @@ class ScriptFrameIter
{
public:
enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
enum State { DONE, SCRIPTED, ION };
enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS };
enum State { DONE, SCRIPTED, JIT };
/*
* Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
@ -1561,21 +1745,21 @@ class ScriptFrameIter
PerThreadData *perThread_;
JSContext *cx_;
SavedOption savedOption_;
ContextOption contextOption_;
State state_;
StackFrame *fp_;
StackSegment *seg_;
jsbytecode *pc_;
InterpreterFrameIterator interpFrames_;
ActivationIterator activations_;
#ifdef JS_ION
ion::IonActivationIterator ionActivations_;
ion::IonFrameIterator ionFrames_;
#endif
Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption);
Data(JSContext *cx, JSRuntime *rt, StackSegment *seg);
Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption,
ContextOption contextOption);
Data(const Data &other);
};
@ -1587,20 +1771,17 @@ class ScriptFrameIter
ion::InlineFrameIterator ionInlineFrames_;
#endif
void poisonRegs();
void popFrame();
void popActivation();
void popInterpreterFrame();
#ifdef JS_ION
void nextIonFrame();
void popIonFrame();
void popBaselineDebuggerFrame();
void nextJitFrame();
void popJitFrame();
#endif
void settleOnNewSegment();
void settleOnNewState();
void startOnSegment(StackSegment *seg);
void settleOnActivation();
public:
ScriptFrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
ScriptFrameIter(JSRuntime *rt, StackSegment &seg);
ScriptFrameIter(JSContext *cx, ContextOption, SavedOption);
ScriptFrameIter(const ScriptFrameIter &iter);
ScriptFrameIter(const Data &data);
@ -1609,9 +1790,6 @@ class ScriptFrameIter
Data *copyData() const;
bool operator==(const ScriptFrameIter &rhs) const;
bool operator!=(const ScriptFrameIter &rhs) const { return !(*this == rhs); }
JSCompartment *compartment() const;
JSScript *script() const {
@ -1619,7 +1797,7 @@ class ScriptFrameIter
if (data_.state_ == SCRIPTED)
return interpFrame()->script();
#ifdef JS_ION
JS_ASSERT(data_.state_ == ION);
JS_ASSERT(data_.state_ == JIT);
if (data_.ionFrames_.isOptimizedJS())
return ionInlineFrames_.script();
return data_.ionFrames_.script();
@ -1627,22 +1805,22 @@ class ScriptFrameIter
return NULL;
#endif
}
bool isIon() const {
bool isJit() const {
JS_ASSERT(!done());
return data_.state_ == ION;
return data_.state_ == JIT;
}
bool isIonOptimizedJS() const {
bool isIon() const {
#ifdef JS_ION
return isIon() && data_.ionFrames_.isOptimizedJS();
return isJit() && data_.ionFrames_.isOptimizedJS();
#else
return false;
#endif
}
bool isIonBaselineJS() const {
bool isBaseline() const {
#ifdef JS_ION
return isIon() && data_.ionFrames_.isBaselineJS();
return isJit() && data_.ionFrames_.isBaselineJS();
#else
return false;
#endif
@ -1665,7 +1843,12 @@ class ScriptFrameIter
* contents of the frame are ignored by Ion code (and GC) and thus
* immediately become garbage and must not be touched directly.
*/
StackFrame *interpFrame() const { JS_ASSERT(data_.state_ == SCRIPTED); return data_.fp_; }
StackFrame *interpFrame() const {
JS_ASSERT(data_.state_ == SCRIPTED);
return data_.interpFrames_.frame();
}
Activation *activation() const { return data_.activations_.activation(); }
jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; }
void updatePcQuadratic();
@ -1710,7 +1893,7 @@ class NonBuiltinScriptFrameIter : public ScriptFrameIter
public:
NonBuiltinScriptFrameIter(JSContext *cx, ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED)
: ScriptFrameIter(cx, opt) { settle(); }
: ScriptFrameIter(cx, opt) { settle(); }
NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data)
: ScriptFrameIter(data)
@ -1719,50 +1902,16 @@ class NonBuiltinScriptFrameIter : public ScriptFrameIter
NonBuiltinScriptFrameIter &operator++() { ScriptFrameIter::operator++(); settle(); return *this; }
};
/*****************************************************************************/
/*
* Blindly iterate over all frames in the current thread's stack. These frames
* can be from different contexts and compartments, so beware. Iterates over
* Ion frames, but does not handle inlined frames.
* can be from different contexts and compartments, so beware.
*/
class AllFramesIter
class AllFramesIter : public ScriptFrameIter
{
public:
AllFramesIter(JSRuntime *rt);
bool done() const { return state_ == DONE; }
AllFramesIter& operator++();
bool isIon() const { return state_ == ION; }
bool isIonOptimizedJS() const {
#ifdef JS_ION
return isIon() && ionFrames_.isOptimizedJS();
#else
return false;
#endif
}
StackFrame *interpFrame() const { JS_ASSERT(state_ == SCRIPTED); return fp_; }
StackSegment *seg() const { return seg_; }
AbstractFramePtr abstractFramePtr() const;
private:
enum State { DONE, SCRIPTED, ION };
#ifdef JS_ION
void popIonFrame();
#endif
void settleOnNewState();
StackSegment *seg_;
StackFrame *fp_;
State state_;
#ifdef JS_ION
ion::IonActivationIterator ionActivations_;
ion::IonFrameIterator ionFrames_;
#endif
AllFramesIter(JSContext *cx)
: ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED)
{}
};
} /* namespace js */

View File

@ -12,6 +12,7 @@
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsTextFormatter.h"
#include "nsCycleCollectorUtils.h"
using namespace mozilla;
@ -153,6 +154,8 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsrefcnt
nsXPCWrappedJS::AddRef(void)
{
if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
MOZ_CRASH();
nsrefcnt cnt = NS_AtomicIncrementRefcnt(mRefCnt);
NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
@ -167,6 +170,8 @@ nsXPCWrappedJS::AddRef(void)
nsrefcnt
nsXPCWrappedJS::Release(void)
{
if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
MOZ_CRASH();
NS_PRECONDITION(0 != mRefCnt, "dup release");
if (mMainThreadOnly && !NS_IsMainThread()) {
@ -274,6 +279,10 @@ nsXPCWrappedJS::GetNewOrUsed(JSObject* aJSObj,
nsISupports* aOuter,
nsXPCWrappedJS** wrapperResult)
{
// Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
MOZ_CRASH();
AutoJSContext cx;
JS::RootedObject jsObj(cx, aJSObj);
JSObject2WrappedJSMap* map;
@ -557,6 +566,10 @@ nsXPCWrappedJS::CallMethod(uint16_t methodIndex,
const XPTMethodDescriptor* info,
nsXPTCMiniVariant* params)
{
// Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread()))
MOZ_CRASH();
if (!IsValid())
return NS_ERROR_UNEXPECTED;
if (NS_IsMainThread() != mMainThread) {

View File

@ -1012,6 +1012,11 @@ nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
!strcmp(aPropertyName, "getInterface")) {
reportable = false;
}
// More special case, see bug 877760.
if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
reportable = false;
}
}
// Try to use the error reporter set on the context to handle this

View File

@ -1,35 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sw=4 ts=4 sts=4 et
* 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/. */
function run_test() {
var tm = Components.classes["@mozilla.org/thread-manager;1"].getService();
var thr = tm.newThread(0);
var foundThreadError = false;
var listener = {
observe: function(message) {
if (/JS function on a different thread/.test(message.message))
foundThreadError = true;
}
};
var cs = Components.classes["@mozilla.org/consoleservice;1"].
getService(Components.interfaces.nsIConsoleService);
cs.registerListener(listener);
thr.dispatch({
run: function() {
do_check_true(false);
}
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
thr.shutdown();
cs.unregisterListener(listener);
do_check_true(foundThreadError);
}

View File

@ -7,7 +7,6 @@ tail =
[test_bug451678.js]
[test_bug596580.js]
[test_bug604362.js]
[test_bug608142.js]
[test_bug641378.js]
[test_bug677864.js]
[test_bug711404.js]

View File

@ -83,12 +83,6 @@ struct LayerPixel {
return RoundToInt(FromCSSRect(aRect, aResolutionX, aResolutionY));
}
static gfx::IntRectTyped<LayerPixel> FromCSSRectRoundOut(const CSSRect& aRect, float aResolutionX, float aResolutionY) {
gfx::RectTyped<LayerPixel> scaled(aRect.x, aRect.y, aRect.width, aRect.height);
scaled.ScaleInverseRoundOut(aResolutionX, aResolutionY);
return gfx::IntRectTyped<LayerPixel>(scaled.x, scaled.y, scaled.width, scaled.height);
}
static CSSIntRect ToCSSIntRectRoundIn(const gfx::IntRectTyped<LayerPixel>& aRect, float aResolutionX, float aResolutionY) {
gfx::IntRectTyped<CSSPixel> ret(aRect.x, aRect.y, aRect.width, aRect.height);
ret.ScaleInverseRoundIn(aResolutionX, aResolutionY);

View File

@ -652,22 +652,12 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
contentBounds.width += scrollableFrame->GetScrollPortRect().width;
contentBounds.height += scrollableFrame->GetScrollPortRect().height;
metrics.mScrollableRect = CSSRect::FromAppUnits(contentBounds);
nsIntRect contentRect = contentBounds.ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
// I'm not sure what units contentRect is really in, hence FromUnknownRect
metrics.mContentRect = LayerIntRect::FromUnknownRect(mozilla::gfx::IntRect(
contentRect.x, contentRect.y, contentRect.width, contentRect.height));
nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
metrics.mScrollOffset = CSSPoint::FromAppUnits(scrollPosition);
}
else {
nsRect contentBounds = aForFrame->GetRect();
metrics.mScrollableRect = CSSRect::FromAppUnits(contentBounds);
nsIntRect contentRect = contentBounds.ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
// I'm not sure what units contentRect is really in, hence FromUnknownRect
metrics.mContentRect = LayerIntRect::FromUnknownRect(mozilla::gfx::IntRect(
contentRect.x, contentRect.y, contentRect.width, contentRect.height));
}
metrics.mScrollId = aScrollId;

View File

@ -262,7 +262,7 @@ nsContentDLF::CreateInstance(const char* aCommand,
nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
if(pluginHost &&
NS_SUCCEEDED(pluginHost->IsPluginEnabledForType(aContentType))) {
pluginHost->PluginExistsForType(aContentType)) {
return CreateDocument(aCommand,
aChannel, aLoadGroup,
aContainer, kPluginDocumentCID,

View File

@ -416,8 +416,8 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
NSIntPixelsToAppUnits(viewport.width, auPerDevPixel) * aXScale,
NSIntPixelsToAppUnits(viewport.height, auPerDevPixel) * aYScale);
view->mContentSize = nsSize(
NSIntPixelsToAppUnits(metrics.mContentRect.width, auPerDevPixel) * aXScale,
NSIntPixelsToAppUnits(metrics.mContentRect.height, auPerDevPixel) * aYScale);
NSFloatPixelsToAppUnits(metrics.mScrollableRect.width, auPerCSSPixel) * aXScale,
NSFloatPixelsToAppUnits(metrics.mScrollableRect.height, auPerCSSPixel) * aYScale);
newContentViews[scrollId] = view;
}

View File

@ -493,10 +493,7 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
case NS_TOUCH_END:
case NS_MOUSE_BUTTON_UP:
if (aEvent->message == NS_TOUCH_END ||
static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton ||
(static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eMiddleButton &&
gMiddlePref)) {
if (ShouldScrollForEvent(aEvent)) {
// stop capturing
AddListener();
DragThumb(false);
@ -511,19 +508,7 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
//return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
return NS_OK;
} else if ((aEvent->message == NS_MOUSE_BUTTON_DOWN &&
static_cast<nsMouseEvent*>(aEvent)->button ==
nsMouseEvent::eLeftButton &&
#ifdef XP_MACOSX
// On Mac the option key inverts the scroll-to-here preference.
(static_cast<nsMouseEvent*>(aEvent)->IsAlt() != GetScrollToClick())) ||
#else
(static_cast<nsMouseEvent*>(aEvent)->IsShift() != GetScrollToClick())) ||
#endif
(gMiddlePref && aEvent->message == NS_MOUSE_BUTTON_DOWN &&
static_cast<nsMouseEvent*>(aEvent)->button ==
nsMouseEvent::eMiddleButton) ||
(aEvent->message == NS_TOUCH_START && GetScrollToClick())) {
} else if (ShouldScrollToClickForEvent(aEvent)) {
nsPoint eventPoint;
if (!GetEventPoint(aEvent, eventPoint)) {
return NS_OK;
@ -573,26 +558,22 @@ nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
bool
nsSliderFrame::GetScrollToClick()
{
// if there is no parent scrollbar, check the movetoclick attribute. If set
// to true, always scroll to the click point. If false, never do this.
// Otherwise, the default is true on Mac and false on other platforms.
if (GetScrollbar() == this)
#ifdef XP_MACOSX
return !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
nsGkAtoms::_false, eCaseMatters);
// if there was no scrollbar, always scroll on click
bool scrollToClick = false;
int32_t scrollToClickMetric;
nsresult rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick,
&scrollToClickMetric);
if (NS_SUCCEEDED(rv) && scrollToClickMetric == 1)
scrollToClick = true;
return scrollToClick;
if (GetScrollbar() != this) {
return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick, false);
}
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
nsGkAtoms::_true, eCaseMatters)) {
return true;
}
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
nsGkAtoms::_false, eCaseMatters)) {
return false;
}
#ifdef XP_MACOSX
return true;
#else
return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
nsGkAtoms::_true, eCaseMatters);
return false;
#endif
}
@ -842,37 +823,24 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
nsGkAtoms::_true, eCaseMatters))
return NS_OK;
bool isHorizontal = IsHorizontal();
bool scrollToClick = false;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent));
if (mouseEvent) {
uint16_t button = 0;
mouseEvent->GetButton(&button);
if (!(button == 0 || (button == 1 && gMiddlePref)))
return NS_OK;
#ifndef XP_MACOSX
// On Mac there's no scroll-to-here when clicking the thumb
mouseEvent->GetShiftKey(&scrollToClick);
if (button != 0) {
scrollToClick = true;
}
#endif
}
nsGUIEvent *event = static_cast<nsGUIEvent*>(aEvent->GetInternalNSEvent());
if (!ShouldScrollForEvent(event)) {
return NS_OK;
}
nsPoint pt;
if (!GetEventPoint(event, pt)) {
return NS_OK;
}
bool isHorizontal = IsHorizontal();
nscoord pos = isHorizontal ? pt.x : pt.y;
// If shift click or middle button, first
// place the middle of the slider thumb under the click
// If we should scroll-to-click, first place the middle of the slider thumb
// under the mouse.
nsCOMPtr<nsIContent> scrollbar;
nscoord newpos = pos;
bool scrollToClick = ShouldScrollToClickForEvent(event);
if (scrollToClick) {
// adjust so that the middle of the thumb is placed under the click
nsIFrame* thumbFrame = mFrames.FirstChild();
@ -969,24 +937,92 @@ nsSliderFrame::RemoveListener()
RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, false);
}
bool
nsSliderFrame::ShouldScrollForEvent(nsGUIEvent* aEvent)
{
switch (aEvent->message) {
case NS_TOUCH_START:
case NS_TOUCH_END:
return true;
case NS_MOUSE_BUTTON_DOWN:
case NS_MOUSE_BUTTON_UP: {
uint16_t button = static_cast<nsMouseEvent*>(aEvent)->button;
return (button == nsMouseEvent::eLeftButton) ||
(button == nsMouseEvent::eMiddleButton && gMiddlePref);
}
default:
return false;
}
}
bool
nsSliderFrame::ShouldScrollToClickForEvent(nsGUIEvent* aEvent)
{
if (!ShouldScrollForEvent(aEvent)) {
return false;
}
if (aEvent->message == NS_TOUCH_START) {
return GetScrollToClick();
}
if (aEvent->message != NS_MOUSE_BUTTON_DOWN) {
return false;
}
#ifdef XP_MACOSX
// On Mac, clicking the scrollbar thumb should never scroll to click.
if (IsEventOverThumb(aEvent)) {
return false;
}
#endif
nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
if (mouseEvent->button == nsMouseEvent::eLeftButton) {
#ifdef XP_MACOSX
bool invertPref = mouseEvent->IsAlt();
#else
bool invertPref = mouseEvent->IsShift();
#endif
return GetScrollToClick() != invertPref;
}
return true;
}
bool
nsSliderFrame::IsEventOverThumb(nsGUIEvent* aEvent)
{
nsIFrame* thumbFrame = mFrames.FirstChild();
if (!thumbFrame) {
return false;
}
nsPoint eventPoint;
if (!GetEventPoint(aEvent, eventPoint)) {
return false;
}
bool isHorizontal = IsHorizontal();
nsRect thumbRect = thumbFrame->GetRect();
nscoord eventPos = isHorizontal ? eventPoint.x : eventPoint.y;
nscoord thumbStart = isHorizontal ? thumbRect.x : thumbRect.y;
nscoord thumbEnd = isHorizontal ? thumbRect.XMost() : thumbRect.YMost();
return eventPos >= thumbStart && eventPos < thumbEnd;
}
NS_IMETHODIMP
nsSliderFrame::HandlePress(nsPresContext* aPresContext,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
if (aEvent->message == NS_TOUCH_START && GetScrollToClick()) {
if (!ShouldScrollForEvent(aEvent) || ShouldScrollToClickForEvent(aEvent)) {
return NS_OK;
}
if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
#ifdef XP_MACOSX
// On Mac the option key inverts the scroll-to-here preference.
if (((nsMouseEvent *)aEvent)->IsAlt() != GetScrollToClick()) {
#else
if (((nsMouseEvent *)aEvent)->IsShift() != GetScrollToClick()) {
#endif
return NS_OK;
}
if (IsEventOverThumb(aEvent)) {
return NS_OK;
}
nsIFrame* thumbFrame = mFrames.FirstChild();

View File

@ -127,6 +127,9 @@ private:
bool GetScrollToClick();
nsIFrame* GetScrollbar();
bool ShouldScrollForEvent(nsGUIEvent* aEvent);
bool ShouldScrollToClickForEvent(nsGUIEvent* aEvent);
bool IsEventOverThumb(nsGUIEvent* aEvent);
void PageUpDown(nscoord change);
void SetCurrentThumbPosition(nsIContent* aScrollbar, nscoord aNewPos, bool aIsSmooth,

View File

@ -553,18 +553,17 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
* this function is invoked on; and this function will always be called prior to syncViewportInfo.
*/
public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
float pageLeft, float pageTop, float pageRight, float pageBottom,
float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
synchronized (getLock()) {
ImmutableViewportMetrics currentMetrics = getViewportMetrics();
Tab tab = Tabs.getInstance().getSelectedTab();
RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
final ImmutableViewportMetrics newMetrics = currentMetrics
.setViewportOrigin(offsetX, offsetY)
.setZoomFactor(zoom)
.setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom),
new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom))
.setPageRect(RectUtils.scale(cssPageRect, zoom), cssPageRect)
.setIsRTL(tab.getIsRTL());
// Since we have switched to displaying a different document, we need to update any
// viewport-related state we have lying around. This includes mGeckoViewport and
@ -690,9 +689,8 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
boolean isFirstPaint)
{
if (isFirstPaint) {
RectF pageRect = RectUtils.scale(new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom), zoom);
setFirstPaintViewport(offsetX, offsetY, zoom, pageRect.left, pageRect.top, pageRect.right,
pageRect.bottom, cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
setFirstPaintViewport(offsetX, offsetY, zoom,
cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
}
return syncViewportInfo(x, y, width, height, resolution, layersUpdated);

View File

@ -30,12 +30,12 @@ public class testMasterPassword extends PixelTest {
// Look for the 'Settings' menu if this device/OS uses it
selectMenuItem("Settings");
if (dev.type.equals("tablet")) {
mSolo.waitForText("Privacy & Security");
waitForText("Privacy & Security");
mSolo.clickOnText("Privacy & Security");
}
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mSolo.waitForText("^Create Master Password$");
waitForText("^Create Master Password$");
// Verify that the OK button is not activated until both fields are filled
closeTabletKeyboard();
@ -59,9 +59,9 @@ public class testMasterPassword extends PixelTest {
mSolo.clearEditText(1);
mSolo.clickOnEditText(1);
mActions.sendKeys(password);
mSolo.waitForText("^Cancel$");
waitForText("^Cancel$");
mSolo.clickOnText("^Cancel$");
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mAsserter.ok(mSolo.waitForText("^Create Master Password$"), "Checking if no password was set if the action was canceled", "No password was set");
@ -72,16 +72,15 @@ public class testMasterPassword extends PixelTest {
mActions.sendKeys(password);
// Verify that the input characters are converted to dots automatically
mAsserter.ok(mSolo.waitForText("."), "waiting to convert the letters in dots", "The letters are converted in dots");
mAsserter.ok(waitForText("."), "waiting to convert the letters in dots", "The letters are converted in dots");
mSolo.clickOnButton("OK");
// Verify that the Master Password was set
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mAsserter.ok(mSolo.searchText("Remove Master Password"), "Checking if the password is enabled", "The password is enabled");
closeTabletKeyboard();
mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Go back to settings menu
mSolo.waitForText("^Settings$");
clickOnButton("Cancel"); // Go back to settings menu
waitForText("^Settings$");
mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu
}
@ -90,12 +89,12 @@ public class testMasterPassword extends PixelTest {
// Look for the 'Settings' menu if this device/OS uses it
selectMenuItem("Settings");
if (dev.type.equals("tablet")) {
mSolo.waitForText("Privacy & Security");
waitForText("Privacy & Security");
mSolo.clickOnText("Privacy & Security");
}
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mSolo.waitForText("^Remove Master Password$");
waitForText("^Remove Master Password$");
// Verify that the OK button is not activated if the password field is empty
closeTabletKeyboard();
@ -115,11 +114,10 @@ public class testMasterPassword extends PixelTest {
mSolo.clickOnButton("OK");
// Verify that the Master Password was disabled
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mAsserter.ok(mSolo.waitForText("^Create Master Password$"), "Checking if the password is disabled", "The password is disabled");
toggleVKB(); // Close the VKB
mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Close the Master Password menu
mAsserter.ok(waitForText("^Create Master Password$"), "Checking if the password is disabled", "The password is disabled");
clickOnButton("Cancel"); // Go back to settings menu
}
public void editPasswordField(int i, String password) {
@ -131,7 +129,7 @@ public class testMasterPassword extends PixelTest {
public void noDoorhangerDisplayed(String LOGIN_URL) {
mSolo.waitForText("Browser Blank Page 01 | Enter Search or Address");
loadUrl(LOGIN_URL);
mAsserter.is(mSolo.waitForText("Save password for"), false, "Doorhanger notification is hidden");
mAsserter.is(waitForText("Save password for"), false, "Doorhanger notification is hidden");
}
public void doorhangerDisplayed(String LOGIN_URL) {
@ -153,25 +151,37 @@ public class testMasterPassword extends PixelTest {
// Look for the 'Settings' menu if this device/OS uses it
selectMenuItem("Settings");
if (dev.type.equals("tablet")) {
mSolo.waitForText("Privacy & Security");
waitForText("Privacy & Security");
mSolo.clickOnText("Privacy & Security");
}
// Clear private data
mSolo.waitForText("^Clear private data$");
waitForText("^Clear private data$");
mSolo.clickOnText("^Clear private data$");
mSolo.waitForText("^Clear data$");
mSolo.clickOnText("^Clear data$");
mAsserter.ok(mSolo.waitForText("^Private data cleared$"), "Waiting for private data to be cleared", "Private data is cleared");
waitForText("Browsing & download history"); // Make sure the Clear private data pop-up is displayed
Actions.EventExpecter clearPrivateDataEventExpecter = mActions.expectGeckoEvent("Sanitize:Finished");
if (mSolo.searchText("Clear data") && !mSolo.searchText("Cookies")) {
mSolo.clickOnText("^Clear data$");
clearPrivateDataEventExpecter.blockForEvent();
} else { // For some reason the pop-up was not opened
if (mSolo.searchText("Cookies")) {
mSolo.clickOnText("^Clear private data$");
waitForText("Browsing & download history"); // Make sure the Clear private data pop-up is displayed
mSolo.clickOnText("^Clear data$");
clearPrivateDataEventExpecter.blockForEvent();
} else {
mAsserter.ok(false, "Something happened and the clear data dialog could not be opened", "Failed to clear data");
}
}
// Check that the Master Password isn't disabled by clearing private data
mSolo.waitForText("^Use master password$");
waitForText("^Use master password$");
mSolo.clickOnText("^Use master password$");
mAsserter.ok(mSolo.searchText("^Remove Master Password$"), "Checking if the master password was disabled by clearing private data", "The master password is not disabled by clearing private data");
closeTabletKeyboard();
mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Master Password menu
mSolo.waitForText("^Settings$");
clickOnButton("Cancel"); // Close the Master Password menu
waitForText("^Settings$");
mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu
waitForText("Browser Blank Page 01"); // Make sure the Settings Menu has been closed
}
public void verifyLoginPage(String password, String badPassword) {
@ -206,7 +216,7 @@ public class testMasterPassword extends PixelTest {
mSolo.clickOnButton(item);
doorhangerDisplayed(LOGIN_URL);// Check that the doorhanger is displayed again
mSolo.clickOnButton(item);
mSolo.waitForText("Browser Blank Page 01");
waitForText("Browser Blank Page 01");
}
}
}

View File

@ -430,8 +430,6 @@ nsOutputStreamTransport::IsNonBlocking(bool *result)
// nsStreamTransportService
//-----------------------------------------------------------------------------
bool nsStreamTransportService::sHasBeenShutdown = false;
nsStreamTransportService::~nsStreamTransportService()
{
NS_ASSERTION(!mPool, "thread pool wasn't shutdown");
@ -440,11 +438,6 @@ nsStreamTransportService::~nsStreamTransportService()
nsresult
nsStreamTransportService::Init()
{
if (sHasBeenShutdown) {
// Prevent any attempt at resurrection
// (see bug 845190)
return NS_ERROR_NOT_AVAILABLE;
}
mPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
NS_ENSURE_STATE(mPool);
@ -515,7 +508,7 @@ nsStreamTransportService::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data)
{
NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
sHasBeenShutdown = true;
if (mPool) {
mPool->Shutdown();
mPool = nullptr;

View File

@ -27,10 +27,4 @@ private:
~nsStreamTransportService();
nsCOMPtr<nsIThreadPool> mPool;
/**
* |true| if we have shutdown once already, in which
* case we should reject any attempt to resurrect.
*/
static bool sHasBeenShutdown;
};

View File

@ -111,11 +111,6 @@ let clone = function clone(object, refs = noRefs) {
return result;
};
/**
* A shared constant used to normalize a set of options to nothing.
*/
const noOptions = {};
let worker = new PromiseWorker(
"resource://gre/modules/osfile/osfile_async_worker.js", LOG);
let Scheduler = {
@ -313,7 +308,7 @@ File.prototype = {
* @resolves {number} The number of bytes effectively read.
* @rejects {OS.File.Error}
*/
readTo: function readTo(buffer, options = noOptions) {
readTo: function readTo(buffer, options = {}) {
// If |buffer| is a typed array and there is no |bytes| options, we
// need to extract the |byteLength| now, as it will be lost by
// communication
@ -351,7 +346,7 @@ File.prototype = {
*
* @return {number} The number of bytes actually written.
*/
write: function write(buffer, options = noOptions) {
write: function write(buffer, options = {}) {
// If |buffer| is a typed array and there is no |bytes| options,
// we need to extract the |byteLength| now, as it will be lost
// by communication
@ -652,7 +647,7 @@ File.exists = function exists(path) {
* @return {promise}
* @resolves {number} The number of bytes actually written.
*/
File.writeAtomic = function writeAtomic(path, buffer, options = noOptions) {
File.writeAtomic = function writeAtomic(path, buffer, options = {}) {
// Copy |options| to avoid modifying the original object but preserve the
// reference to |outExecutionDuration| option if it is passed.
options = clone(options, ["outExecutionDuration"]);

View File

@ -16,9 +16,6 @@ if (typeof Components != "undefined") {
let LOG = exports.OS.Shared.LOG.bind(OS.Shared, "Shared front-end");
const noOptions = {};
/**
* Code shared by implementations of File.
*
@ -80,7 +77,7 @@ AbstractFile.prototype = {
* @return {number} The number of bytes actually read, which may be
* less than |bytes| if the file did not contain that many bytes left.
*/
readTo: function readTo(buffer, options = noOptions) {
readTo: function readTo(buffer, options = {}) {
let {ptr, bytes} = AbstractFile.normalizeToPointer(buffer, options.bytes);
let pos = 0;
while (pos < bytes) {
@ -112,7 +109,7 @@ AbstractFile.prototype = {
*
* @return {number} The number of bytes actually written.
*/
write: function write(buffer, options = noOptions) {
write: function write(buffer, options = {}) {
let {ptr, bytes} = AbstractFile.normalizeToPointer(buffer, options.bytes);
@ -340,7 +337,7 @@ AbstractFile.read = function read(path, bytes) {
* @return {number} The number of bytes actually written.
*/
AbstractFile.writeAtomic =
function writeAtomic(path, buffer, options = noOptions) {
function writeAtomic(path, buffer, options = {}) {
// Verify that path is defined and of the correct type
if (typeof path != "string" || path == "") {

View File

@ -166,10 +166,6 @@
throw_on_negative("flush", UnixFile.fsync(this.fd));
};
// Constant used to normalize options.
const noOptions = {};
// The default unix mode for opening (0600)
const DEFAULT_UNIX_MODE = 384;
@ -216,7 +212,7 @@
* @return {File} A file object.
* @throws {OS.File.Error} If the file could not be opened.
*/
File.open = function Unix_open(path, mode, options = noOptions) {
File.open = function Unix_open(path, mode, options = {}) {
let omode = options.unixMode || DEFAULT_UNIX_MODE;
let flags;
if (options.unixFlags) {
@ -287,7 +283,7 @@
* - {bool} ignoreAbsent If |true|, do not fail if the
* directory does not exist yet.
*/
File.removeEmptyDir = function removeEmptyDir(path, options = noOptions) {
File.removeEmptyDir = function removeEmptyDir(path, options = {}) {
let result = UnixFile.rmdir(path);
if (result == -1) {
if (options.ignoreAbsent && ctypes.errno == Const.ENOENT) {
@ -316,7 +312,7 @@
* - {bool} ignoreExisting If |true|, do not fail if the
* directory already exists.
*/
File.makeDir = function makeDir(path, options = noOptions) {
File.makeDir = function makeDir(path, options = {}) {
let omode = options.unixMode || DEFAULT_UNIX_MODE_DIR;
let result = UnixFile.mkdir(path, omode);
if (result != -1 ||
@ -383,7 +379,7 @@
// This implementation uses |copyfile(3)|, from the BSD library.
// Adding copying of hierarchies and/or attributes is just a flag
// away.
File.copy = function copyfile(sourcePath, destPath, options = noOptions) {
File.copy = function copyfile(sourcePath, destPath, options = {}) {
let flags = Const.COPYFILE_DATA;
if (options.noOverwrite) {
flags |= Const.COPYFILE_EXCL;
@ -421,7 +417,7 @@
let pump_buffer = null;
// An implementation of |pump| using |read|/|write|
let pump_userland = function pump_userland(source, dest, options = noOptions) {
let pump_userland = function pump_userland(source, dest, options = {}) {
let bufSize = options.bufSize || 4096;
let nbytes = options.nbytes || Infinity;
if (!pump_buffer || pump_buffer.length < bufSize) {
@ -457,7 +453,7 @@
const BUFSIZE = 1 << 17;
// An implementation of |pump| using |splice| (for Linux/Android)
pump = function pump_splice(source, dest, options = noOptions) {
pump = function pump_splice(source, dest, options = {}) {
let nbytes = options.nbytes || Infinity;
let pipe = [];
throw_on_negative("pump", UnixFile.pipe(pipe));
@ -521,7 +517,7 @@
// Implement |copy| using |pump|.
// This implementation would require some work before being able to
// copy directories
File.copy = function copy(sourcePath, destPath, options = noOptions) {
File.copy = function copy(sourcePath, destPath, options = {}) {
let source, dest;
let result;
try {
@ -546,7 +542,7 @@
// Implement |move| using |rename| (wherever possible) or |copy|
// (if files are on distinct devices).
File.move = function move(sourcePath, destPath, options = noOptions) {
File.move = function move(sourcePath, destPath, options = {}) {
// An implementation using |rename| whenever possible or
// |File.pump| when required, for other Unices.
// It can move directories on one file system, not
@ -795,7 +791,7 @@
*
* @return {File.Information}
*/
File.stat = function stat(path, options = noOptions) {
File.stat = function stat(path, options = {}) {
if (options.unixNoFollowingLinks) {
throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr));
} else {

View File

@ -188,9 +188,6 @@
throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd));
};
// Constant used to normalize options.
const noOptions = {};
// The default sharing mode for opening files: files are not
// locked against being reopened for reading/writing or against
// being deleted by the same process or another process.
@ -255,7 +252,7 @@
* @return {File} A file object.
* @throws {OS.File.Error} If the file could not be opened.
*/
File.open = function Win_open(path, mode = noOptions, options = noOptions) {
File.open = function Win_open(path, mode = {}, options = {}) {
let share = options.winShare || DEFAULT_SHARE;
let security = options.winSecurity || null;
let flags = options.winFlags || DEFAULT_FLAGS;
@ -347,7 +344,7 @@
* - {bool} ignoreAbsent If |true|, do not fail if the
* directory does not exist yet.
*/
File.removeEmptyDir = function removeEmptyDir(path, options = noOptions) {
File.removeEmptyDir = function removeEmptyDir(path, options = {}) {
let result = WinFile.RemoveDirectory(path);
if (!result) {
if (options.ignoreAbsent &&
@ -372,7 +369,7 @@
* - {bool} ignoreExisting If |true|, do not fail if the
* directory already exists.
*/
File.makeDir = function makeDir(path, options = noOptions) {
File.makeDir = function makeDir(path, options = {}) {
let security = options.winSecurity || null;
let result = WinFile.CreateDirectory(path, security);
if (result ||
@ -406,7 +403,7 @@
* is unspecified. Metadata may or may not be copied with the file. The
* behavior may not be the same across all platforms.
*/
File.copy = function copy(sourcePath, destPath, options = noOptions) {
File.copy = function copy(sourcePath, destPath, options = {}) {
throw_on_zero("copy",
WinFile.CopyFile(sourcePath, destPath, options.noOverwrite || false)
);
@ -438,7 +435,7 @@
* is unspecified. Metadata may or may not be moved with the file. The
* behavior may not be the same across all platforms.
*/
File.move = function move(sourcePath, destPath, options = noOptions) {
File.move = function move(sourcePath, destPath, options = {}) {
let flags = 0;
if (!options.noCopy) {
flags = Const.MOVEFILE_COPY_ALLOWED;

View File

@ -7,3 +7,5 @@ tail =
[test_osfile_async.js]
[test_profiledir.js]
[test_logging.js]
# bug 845190 - thread pool wasn't shutdown assertions
skip-if = os == "win" && debug

View File

@ -5,6 +5,8 @@ firefox-appdir = browser
[test_nocache.js]
[test_645970.js]
# Bug 845190: Too many intermittent assertions on Linux (ASSERTION: thread pool wasn't shutdown)
skip-if = debug && os == "linux"
[test_identifiers.js]
[test_init_async_multiple.js]
[test_init_async_multiple_then_sync.js]

View File

@ -31,6 +31,7 @@ const PREF_BLOCKLIST_INTERVAL = "extensions.blocklist.interval";
const PREF_BLOCKLIST_LEVEL = "extensions.blocklist.level";
const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal";
const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
const PREF_BLOCKLIST_SUPPRESSUI = "extensions.blocklist.suppressUI";
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale";
const PREF_APP_DISTRIBUTION = "distribution.id";
@ -957,7 +958,7 @@ Blocklist.prototype = {
if (state == oldState)
continue;
if (plugin.blocklisted) {
if (oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
}
@ -978,7 +979,6 @@ Blocklist.prototype = {
});
}
}
plugin.blocklisted = state == Ci.nsIBlocklistService.STATE_BLOCKED;
}
if (addonList.length == 0) {
@ -1027,7 +1027,12 @@ Blocklist.prototype = {
Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed");
}
Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false)
Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false);
if (getPref("getBoolPref", PREF_BLOCKLIST_SUPPRESSUI, false)) {
applyBlocklistChanges();
return;
}
function blocklistUnloadHandler(event) {
if (event.target.location == URI_BLOCKLIST_DIALOG) {

View File

@ -111,6 +111,8 @@ MOCHITEST_BROWSER_RESOURCES = \
plugin_test.html \
redirect.sjs \
releaseNotes.xhtml \
blockNoPlugins.xml \
blockPluginHard.xml \
$(NULL)
MOCHITEST_BROWSER_FILES_PARTS = $(foreach s,MAIN SECONDARY RESOURCES,MOCHITEST_BROWSER_$(s))

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310001">
<emItems>
</emItems>
<pluginItems>
</pluginItems>
</blocklist>

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310000">
<emItems>
</emItems>
<pluginItems>
<pluginItem blockID="p9999">
<match name="filename" exp="libnptest\.so|nptest\.dll|Test\.plugin" />
<versionRange severity="2"></versionRange>
</pluginItem>
</pluginItems>
</blocklist>

View File

@ -7,9 +7,34 @@ let gManagerWindow;
let gTestPluginId;
let gPluginBrowser;
function updateBlocklist(aCallback) {
var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsITimerCallback);
var observer = function() {
aCallback();
Services.obs.removeObserver(observer, "blocklist-updated");
};
Services.obs.addObserver(observer, "blocklist-updated", false);
blocklistNotifier.notify(null);
}
var _originalBlocklistURL = null;
function setAndUpdateBlocklist(aURL, aCallback) {
if (!_originalBlocklistURL) {
_originalBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
}
Services.prefs.setCharPref("extensions.blocklist.url", aURL);
updateBlocklist(aCallback);
}
function resetBlocklist(aCallback) {
Services.prefs.setCharPref("extensions.blocklist.url", _originalBlocklistURL);
}
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref("plugins.click_to_play", true);
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
let pluginTag = getTestPluginTag();
pluginTag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
open_manager("addons://list/plugin", part1);
@ -160,9 +185,13 @@ function part11() {
gBrowser.removeCurrentTab();
let pluginTag = getTestPluginTag();
pluginTag.blocklisted = true; // causes appDisabled to be set
close_manager(gManagerWindow, function() {
open_manager("addons://list/plugin", part12);
// causes appDisabled to be set
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginHard.xml",
function() {
close_manager(gManagerWindow, function() {
open_manager("addons://list/plugin", part12);
});
});
}
@ -182,15 +211,17 @@ function part13() {
let menu = gManagerWindow.document.getElementById("detail-state-menulist");
is_element_hidden(menu, "part13: detail state menu should be hidden");
let pluginTag = getTestPluginTag();
pluginTag.blocklisted = false;
run_next_test();
setAndUpdateBlocklist(gHttpTestRoot + "blockNoPlugins.xml", function() {
run_next_test();
});
}
function end_test() {
Services.prefs.clearUserPref("plugins.click_to_play");
Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
let pluginTag = getTestPluginTag();
pluginTag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
resetBlocklist();
close_manager(gManagerWindow, function() {
finish();
});

View File

@ -186,182 +186,48 @@ var ADDONS = [{
toolkitBlocks: true
}];
var PLUGINS = [{
name: "test_bug449027_1",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_2",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_3",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_4",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_5",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_6",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_7",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_8",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_9",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_10",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_11",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_12",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_13",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: false
}, {
name: "test_bug449027_14",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_15",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_16",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_17",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_18",
version: "5",
blocklisted: false,
start: false,
appBlocks: false,
toolkitBlocks: false
}, {
name: "test_bug449027_19",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_20",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_21",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_22",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_23",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_24",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}, {
name: "test_bug449027_25",
version: "5",
blocklisted: false,
start: false,
appBlocks: true,
toolkitBlocks: true
}];
function MockPluginTag(name, version, start, appBlocks, toolkitBlocks)
{
this.name = name;
this.version = version;
this.start = start;
this.appBlocks = appBlocks;
this.toolkitBlocks = toolkitBlocks;
}
Object.defineProperty(MockPluginTag.prototype, "blocklisted", {
get: function MockPluginTag_getBlocklisted() {
let bls = AM_Cc["@mozilla.org/extensions/blocklist;1"].getService(Ci.nsIBlocklistService);
return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
}
});
var PLUGINS = [
new MockPluginTag("test_bug449027_1", "5", false, false, false),
new MockPluginTag("test_bug449027_2", "5", false, true, false),
new MockPluginTag("test_bug449027_3", "5", false, true, false),
new MockPluginTag("test_bug449027_4", "5", false, false, false),
new MockPluginTag("test_bug449027_5", "5", false, false, false),
new MockPluginTag("test_bug449027_6", "5", false, true, false),
new MockPluginTag("test_bug449027_7", "5", false, true, false),
new MockPluginTag("test_bug449027_8", "5", false, true, false),
new MockPluginTag("test_bug449027_9", "5", false, true, false),
new MockPluginTag("test_bug449027_10", "5", false, true, false),
new MockPluginTag("test_bug449027_11", "5", false, true, false),
new MockPluginTag("test_bug449027_12", "5", false, true, false),
new MockPluginTag("test_bug449027_13", "5", false, true, false),
new MockPluginTag("test_bug449027_14", "5", false, false, false),
new MockPluginTag("test_bug449027_15", "5", false, true, true),
new MockPluginTag("test_bug449027_16", "5", false, true, true),
new MockPluginTag("test_bug449027_17", "5", false, false, false),
new MockPluginTag("test_bug449027_18", "5", false, false, false),
new MockPluginTag("test_bug449027_19", "5", false, true, true),
new MockPluginTag("test_bug449027_20", "5", false, true, true),
new MockPluginTag("test_bug449027_21", "5", false, true, true),
new MockPluginTag("test_bug449027_22", "5", false, true, true),
new MockPluginTag("test_bug449027_23", "5", false, true, true),
new MockPluginTag("test_bug449027_24", "5", false, true, true),
new MockPluginTag("test_bug449027_25", "5", false, true, true)
];
var gCallback = null;
var gTestserver = null;

Some files were not shown because too many files have changed in this diff Show More