mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and inbound
This commit is contained in:
commit
64b2bc1c50
@ -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;"/>
|
||||
|
@ -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) {
|
||||
|
@ -318,6 +318,7 @@ _BROWSER_FILES = \
|
||||
blockPluginVulnerableUpdatable.xml \
|
||||
blockPluginVulnerableNoUpdate.xml \
|
||||
blockNoPlugins.xml \
|
||||
blockPluginHard.xml \
|
||||
browser_utilityOverlay.js \
|
||||
browser_bug676619.js \
|
||||
download_page.html \
|
||||
|
11
browser/base/content/test/blockPluginHard.xml
Normal file
11
browser/base/content/test/blockPluginHard.xml
Normal 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>
|
@ -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");
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"));
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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*
|
||||
|
@ -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 \
|
||||
|
7
content/base/test/chrome/blockNoPlugins.xml
Normal file
7
content/base/test/chrome/blockNoPlugins.xml
Normal 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>
|
11
content/base/test/chrome/blockPluginHard.xml
Normal file
11
content/base/test/chrome/blockPluginHard.xml
Normal 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>
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) &&
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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_);
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
17
js/src/jit-test/tests/basic/cross-context-stack-1.js
Normal file
17
js/src/jit-test/tests/basic/cross-context-stack-1.js
Normal 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");
|
@ -716,7 +716,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
|
||||
ionTop(NULL),
|
||||
ionJSContext(NULL),
|
||||
ionStackLimit(0),
|
||||
ionActivation(NULL),
|
||||
activation_(NULL),
|
||||
asmJSActivationStack_(NULL),
|
||||
suppressGC(0)
|
||||
{}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -258,6 +258,8 @@ enum ParallelBailoutCause {
|
||||
ParallelBailoutUnsupported,
|
||||
ParallelBailoutUnsupportedStringComparison,
|
||||
ParallelBailoutUnsupportedSparseArray,
|
||||
ParallelBailoutRequestedGC,
|
||||
ParallelBailoutRequestedZoneGC,
|
||||
};
|
||||
|
||||
struct ParallelBailoutTrace {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 ®s = 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 ®s)
|
||||
: 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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ®s_;
|
||||
|
||||
public:
|
||||
inline InterpreterActivation(JSContext *cx, StackFrame *entry, FrameRegs ®s);
|
||||
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 */
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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"]);
|
||||
|
@ -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 == "") {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1336406310001">
|
||||
<emItems>
|
||||
</emItems>
|
||||
<pluginItems>
|
||||
</pluginItems>
|
||||
</blocklist>
|
11
toolkit/mozapps/extensions/test/browser/blockPluginHard.xml
Normal file
11
toolkit/mozapps/extensions/test/browser/blockPluginHard.xml
Normal 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>
|
@ -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();
|
||||
});
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user