Merge inbound to m-c.

This commit is contained in:
Ryan VanderMeulen 2013-04-19 13:46:37 -04:00
commit 1c3243798f
274 changed files with 7088 additions and 1732 deletions

View File

@ -282,3 +282,11 @@ label:active,
textarea:active {
background-color: rgba(141, 184, 216, 0.5);
}
#ifdef MOZ_WIDGET_GONK
/* This binding only provide key shortcuts that we can't use on devices */
input,
textarea {
-moz-binding: none !important;
}
#endif

View File

@ -19,7 +19,7 @@ chrome.jar:
content/screen.js (content/screen.js)
content/runapp.js (content/runapp.js)
#endif
content/content.css (content/content.css)
* content/content.css (content/content.css)
content/touchcontrols.css (content/touchcontrols.css)
content/payment.js (content/payment.js)

View File

@ -13,7 +13,12 @@ function test() {
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,foo";
content.location = "data:text/html;charset=utf8,test custom presets in responsive mode";
// This test uses executeSoon() when responsive mode is initialized and when
// it is destroyed such that we get out of the init/destroy loops. If we try
// to init/destroy immediately, without waiting for the next loop, we get
// intermittent test failures.
function startTest() {
// Mocking prompt
@ -27,6 +32,10 @@ function test() {
}
};
registerCleanupFunction(() => Services.prompt = oldPrompt);
info("test started, waiting for responsive mode to activate");
document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
mgr.once("on", onUIOpen);
synthesizeKeyFromKeyTag("key_responsiveUI");
@ -67,6 +76,7 @@ function test() {
instance.menulist.selectedIndex = 1;
info("waiting for responsive mode to turn off");
mgr.once("off", restart);
// We're still in the loop of initializing the responsive mode.
@ -117,14 +127,24 @@ function test() {
deletedPresetB = instance.menulist.selectedItem.getAttribute("label");
instance.removebutton.doCommand();
EventUtils.synthesizeKey("VK_ESCAPE", {});
executeSoon(restartAgain);
info("waiting for responsive mode to turn off");
mgr.once("off", restartAgain);
// We're still in the loop of initializing the responsive mode.
// Let's wait next loop to stop it.
executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {}));
}
function restartAgain() {
synthesizeKeyFromKeyTag("key_responsiveUI");
info("waiting for responsive mode to turn on");
mgr.once("on", () => {
instance = gBrowser.selectedTab.__responsiveUI;
executeSoon(testCustomPresetsNotInListAnymore);
testCustomPresetsNotInListAnymore();
});
// We're still in the loop of destroying the responsive mode.
// Let's wait next loop to start it.
executeSoon(() => synthesizeKeyFromKeyTag("key_responsiveUI"));
}
function testCustomPresetsNotInListAnymore() {
@ -141,8 +161,6 @@ function test() {
delete instance;
gBrowser.removeCurrentTab();
Services.prompt = oldPrompt;
finish();
}
@ -167,8 +185,6 @@ function test() {
let key = document.getElementById(aKeyId);
isnot(key, null, "Successfully retrieved the <key> node");
let modifiersAttr = key.getAttribute("modifiers");
let name = null;
if (key.getAttribute("keycode"))
@ -178,14 +194,6 @@ function test() {
isnot(name, null, "Successfully retrieved keycode/key");
let modifiers = {
shiftKey: modifiersAttr.match("shift"),
ctrlKey: modifiersAttr.match("ctrl"),
altKey: modifiersAttr.match("alt"),
metaKey: modifiersAttr.match("meta"),
accelKey: modifiersAttr.match("accel")
}
EventUtils.synthesizeKey(name, modifiers);
key.doCommand();
}
}

View File

@ -18,18 +18,6 @@
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
this._selectionOverlay.addEventListener('contextmenu', this);
]]>
</constructor>
<destructor>
<![CDATA[
this._selectionOverlay.removeEventListener('contextmenu', this);
]]>
</destructor>
<field name="_selectionOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-inner").parentNode;</field>
<field name="_selectionDebugOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-debug");</field>

View File

@ -393,29 +393,15 @@ var BrowserUI = {
},
closeTab: function closeTab(aTab) {
// If we only have one tab, open a new one
if (Browser.tabs.length === 1 && !StartUI.isStartURI())
Browser.addTab(Browser.getHomePage());
// We only have the start tab
if (Browser.tabs.length === 1)
return;
// If no tab is passed in, assume the current tab
let tab = aTab || Browser.selectedTab;
let tabToClose = tab instanceof XULElement ? Browser.getTabFromChrome(tab) : tab;
Browser.closeTab(tab);
},
animateClosingTab: function animateClosingTab(tabToClose) {
if (this.isTabsOnly) {
Browser.closeTab(tabToClose);
Browser.closeTab(tabToClose, { forceClose: true } );
} else {
let nextTab = Browser.getNextTab(tabToClose);
if (!nextTab)
return;
if (nextTab)
Browser.selectedTab = nextTab;
// Trigger closing animation
tabToClose.chromeTab.setAttribute("closing", "true");
@ -425,7 +411,7 @@ var BrowserUI = {
}
this.setOnTabAnimationEnd(function() {
Browser.closeTab(tabToClose);
Browser.closeTab(tabToClose, { forceClose: true } );
if (wasCollapsed)
ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
});

View File

@ -442,22 +442,18 @@ var Browser = {
if (aBringFront)
this.selectedTab = newTab;
let getAttention = ("getAttention" in params ? params.getAttention : !aBringFront);
let event = document.createEvent("UIEvents");
event.initUIEvent("TabOpen", true, false, window, getAttention);
newTab.chromeTab.dispatchEvent(event);
newTab.browser.messageManager.sendAsyncMessage("Browser:TabOpen");
this._announceNewTab(newTab, params, aBringFront);
return newTab;
},
closeTab: function closeTab(aTab, aOptions) {
let tab = aTab instanceof XULElement ? this.getTabFromChrome(aTab) : aTab;
if (!tab || !this.getNextTab(tab))
if (!tab) {
return;
}
if (aOptions && "forceClose" in aOptions && aOptions.forceClose) {
this._doCloseTab(aTab);
this._doCloseTab(tab);
return;
}
@ -468,10 +464,24 @@ var Browser = {
ContentAreaUtils.saveDocument(this.selectedBrowser.contentWindow.document);
},
/*
* helper for addTab related methods. Fires events related to
* new tab creation.
*/
_announceNewTab: function _announceNewTab(aTab, aParams, aBringFront) {
let getAttention = ("getAttention" in aParams ? aParams.getAttention : !aBringFront);
let event = document.createEvent("UIEvents");
event.initUIEvent("TabOpen", true, false, window, getAttention);
aTab.chromeTab.dispatchEvent(event);
aTab.browser.messageManager.sendAsyncMessage("Browser:TabOpen");
},
_doCloseTab: function _doCloseTab(aTab) {
if (this._tabs.length === 1) {
Browser.addTab(this.getHomePage());
}
let nextTab = this.getNextTab(aTab);
if (!nextTab)
return;
// Tabs owned by the closed tab are now orphaned.
this._tabs.forEach(function(item, index, array) {
@ -918,15 +928,10 @@ var Browser = {
break;
case "Browser:CanUnload:Return": {
if (!json.permit)
return;
// Allow a little delay to not close the target tab while processing
// a message for this particular tab
setTimeout(function(self) {
let tab = self.getTabForBrowser(browser);
self._doCloseTab(tab);
}, 0, this);
if (json.permit) {
let tab = this.getTabForBrowser(browser);
BrowserUI.animateClosingTab(tab);
}
break;
}
case "Browser:ZoomToPoint:Return":
@ -1401,6 +1406,7 @@ function Tab(aURI, aParams) {
this._loading = false;
this._chromeTab = null;
this._metadata = null;
this._eventDeferred = null;
this.owner = null;
@ -1436,6 +1442,10 @@ Tab.prototype = {
return this._metadata || kDefaultMetadata;
},
get pageShowPromise() {
return this._eventDeferred ? this._eventDeferred.promise : null;
},
/** Update browser styles when the viewport metadata changes. */
updateViewportMetadata: function updateViewportMetadata(aMetadata) {
if (aMetadata && aMetadata.autoScale) {
@ -1534,23 +1544,23 @@ Tab.prototype = {
},
create: function create(aURI, aParams) {
this._eventDeferred = Promise.defer();
this._chromeTab = Elements.tabList.addTab();
this._id = Browser.createTabId();
let browser = this._createBrowser(aURI, null);
// Should we fully load the new browser, or wait until later
if ("delayLoad" in aParams && aParams.delayLoad)
return;
try {
let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
let charset = "charset" in aParams ? aParams.charset : null;
browser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData);
} catch(e) {
dump("Error: " + e + "\n");
let self = this;
function onPageShowEvent(aEvent) {
browser.removeEventListener("pageshow", onPageShowEvent);
if (self._eventDeferred) {
self._eventDeferred.resolve(self);
}
self._eventDeferred = null;
}
browser.addEventListener("pageshow", onPageShowEvent, true);
this._loadUsingParams(browser, aURI, aParams);
},
destroy: function destroy() {
@ -1583,6 +1593,14 @@ Tab.prototype = {
browser.__SS_restore = true;
},
_loadUsingParams: function _loadUsingParams(aBrowser, aURI, aParams) {
let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
let charset = "charset" in aParams ? aParams.charset : null;
aBrowser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData);
},
_createBrowser: function _createBrowser(aURI, aInsertBefore) {
if (this._browser)
throw "Browser already exists";

View File

@ -18,10 +18,6 @@ var ContextMenuHandler = {
// Messages we receive from browser
// Command sent over from browser that only we can handle.
addMessageListener("Browser:ContextCommand", this, false);
// InvokeContextAtPoint is sent to us from browser's selection
// overlay when it traps a contextmenu event. In response we
// should invoke context menu logic at the point specified.
addMessageListener("Browser:InvokeContextAtPoint", this, false);
this.popupNode = null;
},

View File

@ -64,6 +64,7 @@ var SelectionHandler = {
addMessageListener("Browser:CaretUpdate", this);
addMessageListener("Browser:SelectionSwitchMode", this);
addMessageListener("Browser:RepositionInfoRequest", this);
addMessageListener("Browser:SelectionHandlerPing", this);
},
shutdown: function shutdown() {
@ -82,6 +83,7 @@ var SelectionHandler = {
removeMessageListener("Browser:CaretUpdate", this);
removeMessageListener("Browser:SelectionSwitchMode", this);
removeMessageListener("Browser:RepositionInfoRequest", this);
removeMessageListener("Browser:SelectionHandlerPing", this);
},
/*************************************************
@ -436,6 +438,10 @@ var SelectionHandler = {
});
},
_onPing: function _onPing(aId) {
sendAsyncMessage("Content:SelectionHandlerPong", { id: aId });
},
/*************************************************
* Selection helpers
*/
@ -474,6 +480,7 @@ var SelectionHandler = {
this._contentOffset = null;
this._domWinUtils = null;
this._targetIsEditable = false;
sendSyncMessage("Content:HandlerShutdown", {});
},
/*
@ -1221,6 +1228,10 @@ var SelectionHandler = {
case "Browser:RepositionInfoRequest":
this._repositionInfoRequest(json);
break;
case "Browser:SelectionHandlerPing":
this._onPing(json.id);
break;
}
},

View File

@ -154,12 +154,6 @@ var FindHelperUI = {
search: function findHelperSearch(aValue) {
this.updateCommands(aValue);
// Don't bother searching if the value is empty
if (aValue == "") {
this.status = null;
return;
}
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue });
},

View File

@ -13,6 +13,8 @@
* padding top: 6
*/
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/commonjs/sdk/core/promise.js");
// Y axis scroll distance that will disable this module and cancel selection
const kDisableOnScrollDistance = 25;
@ -251,7 +253,6 @@ var SelectionHelperUI = {
_target: null,
_movement: { active: false, x:0, y: 0 },
_activeSelectionRect: null,
_selectionHandlerActive: false,
_selectionMarkIds: [],
_targetIsEditable: false,
@ -287,17 +288,87 @@ var SelectionHelperUI = {
/*
* isActive (prop)
*
* Determines if an edit session is currently active.
* Determines if a selection edit session is currently active.
*/
get isActive() {
return (this._msgTarget &&
this._selectionHandlerActive);
return this._msgTarget ? true : false;
},
/*
* isSelectionUIVisible (prop)
*
* Determines if edit session monocles are visible. Useful
* in checking if selection handler is setup for tests.
*/
get isSelectionUIVisible() {
if (!this._msgTarget || !this._startMark)
return false;
return this._startMark.visible;
},
/*
* isCaretUIVisible (prop)
*
* Determines if caret browsing monocle is visible. Useful
* in checking if selection handler is setup for tests.
*/
get isCaretUIVisible() {
if (!this._msgTarget || !this._caretMark)
return false;
return this._caretMark.visible;
},
/*
* hasActiveDrag (prop)
*
* Determines if a marker is actively being dragged (missing call
* to markerDragStop). Useful in checking if selection handler is
* setup for tests.
*/
get hasActiveDrag() {
if (!this._msgTarget)
return false;
if ((this._caretMark && this._caretMark.dragging) ||
(this._startMark && this._startMark.dragging) ||
(this._endMark && this._endMark.dragging))
return true;
return false;
},
/*
* Public apis
*/
/*
* pingSelectionHandler
*
* Ping the SelectionHandler and wait for the right response. Insures
* all previous messages have been received. Useful in checking if
* selection handler is setup for tests.
*
* @return a promise
*/
pingSelectionHandler: function pingSelectionHandler() {
if (!this.isActive)
return null;
if (this._pingCount == undefined) {
this._pingCount = 0;
this._pingArray = [];
}
this._pingCount++;
let deferred = Promise.defer();
this._pingArray.push({
id: this._pingCount,
deferred: deferred
});
this._sendAsyncMessage("Browser:SelectionHandlerPing", { id: this._pingCount });
return deferred.promise;
},
/*
* openEditSession
*
@ -316,7 +387,6 @@ var SelectionHelperUI = {
// Send this over to SelectionHandler in content, they'll message us
// back with information on the current selection. SelectionStart
// takes client coordinates.
this._selectionHandlerActive = false;
this._sendAsyncMessage("Browser:SelectionStart", {
xPos: aX,
yPos: aY
@ -340,7 +410,6 @@ var SelectionHelperUI = {
// Send this over to SelectionHandler in content, they'll message us
// back with information on the current selection. SelectionAttach
// takes client coordinates.
this._selectionHandlerActive = false;
this._sendAsyncMessage("Browser:SelectionAttach", {
xPos: aX,
yPos: aY
@ -372,7 +441,6 @@ var SelectionHelperUI = {
this._lastPoint = { xPos: aX, yPos: aY };
this._selectionHandlerActive = false;
this._sendAsyncMessage("Browser:CaretAttach", {
xPos: aX,
yPos: aY
@ -400,11 +468,15 @@ var SelectionHelperUI = {
* clear any selection. optional, the default is false.
*/
closeEditSession: function closeEditSession(aClearSelection) {
if (!this.isActive) {
return;
}
// This will callback in _selectionHandlerShutdown in
// which we will call _shutdown().
let clearSelection = aClearSelection || false;
this._sendAsyncMessage("Browser:SelectionClose", {
clearSelection: clearSelection
});
this._shutdown();
},
/*
@ -427,6 +499,8 @@ var SelectionHelperUI = {
messageManager.addMessageListener("Content:SelectionCopied", this);
messageManager.addMessageListener("Content:SelectionFail", this);
messageManager.addMessageListener("Content:SelectionDebugRect", this);
messageManager.addMessageListener("Content:HandlerShutdown", this);
messageManager.addMessageListener("Content:SelectionHandlerPong", this);
window.addEventListener("keypress", this, true);
window.addEventListener("click", this, false);
@ -451,6 +525,8 @@ var SelectionHelperUI = {
messageManager.removeMessageListener("Content:SelectionCopied", this);
messageManager.removeMessageListener("Content:SelectionFail", this);
messageManager.removeMessageListener("Content:SelectionDebugRect", this);
messageManager.removeMessageListener("Content:HandlerShutdown", this);
messageManager.removeMessageListener("Content:SelectionHandlerPong", this);
window.removeEventListener("keypress", this, true);
window.removeEventListener("click", this, false);
@ -472,7 +548,6 @@ var SelectionHelperUI = {
this._selectionMarkIds = [];
this._msgTarget = null;
this._activeSelectionRect = null;
this._selectionHandlerActive = false;
this.overlay.displayDebugLayer = false;
this.overlay.enabled = false;
@ -551,7 +626,6 @@ var SelectionHelperUI = {
this.closeEditSession(true);
// Reset some of our state
this._selectionHandlerActive = false;
this._activeSelectionRect = null;
// Reset the monocles
@ -808,6 +882,14 @@ var SelectionHelperUI = {
aMsg.color, aMsg.fill, aMsg.id);
},
_selectionHandlerShutdown: function _selectionHandlerShutdown() {
this._shutdown();
},
/*
* Message handlers
*/
_onSelectionCopied: function _onSelectionCopied(json) {
this.closeEditSession(true);
},
@ -851,6 +933,21 @@ var SelectionHelperUI = {
this.closeEditSession();
},
/*
* _onPong
*
* Handles the closure of promise we return when we send a ping
* to SelectionHandler in pingSelectionHandler. Testing use.
*/
_onPong: function _onPong(aId) {
let ping = this._pingArray.pop();
if (ping.id != aId) {
ping.deferred.reject(
new Error("Selection module's pong doesn't match our last ping."));
}
ping.deferred.resolve();
},
/*
* Events
*/
@ -902,8 +999,11 @@ var SelectionHelperUI = {
this._onResize(aEvent);
break;
case "ZoomChanged":
case "URLChanged":
this._shutdown();
break;
case "ZoomChanged":
case "MozPrecisePointer":
this.closeEditSession(true);
break;
@ -928,20 +1028,23 @@ var SelectionHelperUI = {
let json = aMessage.json;
switch (aMessage.name) {
case "Content:SelectionFail":
this._selectionHandlerActive = false;
this._onSelectionFail();
break;
case "Content:SelectionRange":
this._selectionHandlerActive = true;
this._onSelectionRangeChange(json);
break;
case "Content:SelectionCopied":
this._selectionHandlerActive = true;
this._onSelectionCopied(json);
break;
case "Content:SelectionDebugRect":
this._onDebugRectRequest(json);
break;
case "Content:HandlerShutdown":
this._selectionHandlerShutdown();
break;
case "Content:SelectionHandlerPong":
this._onPong(json.id);
break;
}
},

View File

@ -34,6 +34,17 @@ BROWSER_TESTS = \
browser_tabs.js \
$(NULL)
ifndef MOZ_DEBUG
BROWSER_TESTS += \
browser_selection_basic.js \
browser_selection_basic.html \
browser_selection_textarea.js \
browser_selection_textarea.html \
browser_selection_frame_content.js \
browser_selection_frame_content.html \
$(NULL)
endif
BROWSER_TEST_RESOURCES = \
res/image01.png \
$(NULL)

View File

@ -20,12 +20,6 @@ function debugClipFlavors(aClip)
}
}
// XXX won't work with out of process content
function emptyClipboard() {
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
}
function checkContextMenuPositionRange(aElement, aMinLeft, aMaxLeft, aMinTop, aMaxTop) {
ok(aElement.left > aMinLeft && aElement.left < aMaxLeft,
"Left position is " + aElement.left + ", expected between " + aMinLeft + " and " + aMaxLeft);

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<style>
#text { position: absolute; left: 1em; bottom: 1em; }
</style>
</head>
<body>
<div style="margin: 0; padding: 0; max-width:500px;">
There was nothing so VERY remarkable in that; nor did Alice think it so VERY much out of
the way to hear the Rabbit say to itself, `Oh dear! Oh dear! I shall be late!' (when she
thought it over afterwards, it occurred to her that she ought to have wondered at this,
but at the time it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
OUT OF ITS WAISTCOAT- POCKET, and looked at it, and then hurried on, Alice started to her
feet, for it flashed across her mind that she had never before seen a rabbit with either a
waistcoat-pocket, or a watch to take out of it, and burning with curiosity, she ran across
the field after it, and fortunately was just in time to see it pop down a large
rabbit-hole under the hedge.
<br>
<br>
In another moment down went Alice after it, never once considering how in the world she
was to get out again.
<br>
<br>
The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly
down, so suddenly that Alice had not a moment to think about stopping herself before she
found herself falling down a very deep well.
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</div>
</body>
</html>

View File

@ -0,0 +1,425 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gWindow = null;
var gFrame = null;
const kMarkerOffsetY = 12;
const kCommonWaitMs = 5000;
const kCommonPollMs = 100;
///////////////////////////////////////////////////
// content (non-editable) tests
///////////////////////////////////////////////////
function setUpAndTearDown() {
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
}
gTests.push({
desc: "normalize browser",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
info(chromeRoot + "browser_selection_basic.html");
yield addTab(chromeRoot + "browser_selection_basic.html");
yield waitForCondition(function () {
return !StartUI.isStartPageVisible;
}, 10000, 100);
gWindow = Browser.selectedTab.browser.contentWindow;
InputSourceHelper.isPrecise = false;
},
});
gTests.push({
desc: "tap-hold to select",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
sendContextMenuClick(30, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(getTrimmedSelection(gWindow).toString(), "There", "selection test");
},
});
gTests.push({
desc: "double-tap to select",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
sendDoubleTap(gWindow, 30, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(getTrimmedSelection(gWindow).toString(), "There", "selection test");
},
});
gTests.push({
desc: "appbar interactions",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
sendContextMenuClick(100, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gWindow).toString(), "nothing", "selection test");
yield fireAppBarDisplayEvent();
ok(ContextUI.isVisible, true, "appbar visible");
yield hideContextUI();
ok(!ContextUI.isVisible, true, "appbar hidden");
},
});
gTests.push({
desc: "simple drag selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
yield waitForMs(100);
sendContextMenuClick(100, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gWindow).toString(), "nothing", "selection test");
let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
let touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gWindow).toString(), "nothing so VERY", "selection test");
},
});
gTests.push({
desc: "expand / collapse selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
sendContextMenuClick(30, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "initial active");
is(getTrimmedSelection(gWindow).toString(), "There", "initial selection test");
for (let count = 0; count < 5; count++) {
let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
let touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 550, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(getTrimmedSelection(gWindow).toString(),
"There was nothing so VERY remarkable in that; nor did Alice think it so",
"long selection test");
is(SelectionHelperUI.isActive, true, "selection active");
touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 40, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gWindow).toString(), "There was", "short selection test");
}
},
});
gTests.push({
desc: "expand / collapse selection scolled content",
setUp: setUpAndTearDown,
run: function test() {
let scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, 200);
yield scrollPromise;
ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error");
sendContextMenuClick(106, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gWindow).toString(), "moment", "selection test");
let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
let touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 550, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(getTrimmedSelection(gWindow).toString(),
"moment down went Alice after it, never once considering how in",
"selection test");
is(SelectionHelperUI.isActive, true, "selection active");
touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 150, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(getTrimmedSelection(gWindow).toString(), "moment down went", "selection test");
touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 550, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(getTrimmedSelection(gWindow).toString(),
"moment down went Alice after it, never once considering how in",
"selection test");
touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 160, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(getTrimmedSelection(gWindow).toString(),
"moment down went",
"selection test");
},
tearDown: function tearDown() {
let scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, -200);
yield scrollPromise;
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
},
});
gTests.push({
desc: "scroll disables",
setUp: setUpAndTearDown,
run: function test() {
sendContextMenuClick(100, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
// scroll page
sendTouchDrag(gWindow,
400,
400,
400,
200);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// active state - should be disabled after a page scroll
is(SelectionHelperUI.isActive, false, "selection inactive");
},
tearDown: function tearDown() {
EventUtils.synthesizeKey("VK_HOME", {}, gWindow);
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
},
});
/*
disable until bug 860248 is addressed.
gTests.push({
desc: "double-tap copy text in content",
setUp: setUpHelper,
run: function test() {
sendContextMenuClick(30, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
sendDoubleTap(gWindow, 30, 20);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// check copy text results
let text = SpecialPowers.getClipboardData("text/unicode").trim();
is(text, "There", "copy text test");
// check for active selection
is(getTrimmedSelection(gWindow).toString(), "", "selection test");
},
tearDown: tearDownHelper,
});
gTests.push({
desc: "double-tap copy text in scrolled content",
setUp: setUpHelper,
run: function test() {
let scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, 200);
yield scrollPromise;
ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error");
sendContextMenuClick(30, 100);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
sendDoubleTap(gWindow, 42, 100);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// check copy text results
let text = SpecialPowers.getClipboardData("text/unicode");
is(text, "suddenly", "copy text test");
// check for active selection
is(getTrimmedSelection(gWindow).toString(), "", "selection test");
},
tearDown: function tearDown() {
emptyClipboard();
clearSelection(gWindow);
let scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, -200);
yield scrollPromise;
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
},
});
gTests.push({
desc: "single clicks on selection in non-editable content",
setUp: setUpHelper,
run: function test() {
sendContextMenuClick(100, 20);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// active state
is(SelectionHelperUI.isActive, true, "selection active");
let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
let touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
// active state
is(SelectionHelperUI.isActive, true, "selection active");
// click on selected text - nothing should change
sendTap(gWindow, 240, 20);
is(SelectionHelperUI.isActive, true, "selection active");
// click outside the text - nothing should change
sendTap(gWindow, 197, 119);
is(SelectionHelperUI.isActive, true, "selection active");
},
tearDown: tearDownHelper,
});
*/
function test() {
if (!isLandscapeMode()) {
todo(false, "browser_selection_tests need landscape mode to run.");
return;
}
requestLongerTimeout(3);
runTests();
}

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<iframe id="frame1" width="800" height="600" src="text-block.html"></iframe>
<br />
<br />
Hello there. <a id="rlink1" href="#hello">Hi!</a>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</div>
</body>
</html>

View File

@ -0,0 +1,227 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gWindow = null;
var gFrame = null;
const kMarkerOffsetY = 12;
const kCommonWaitMs = 5000;
const kCommonPollMs = 100;
///////////////////////////////////////////////////
// sub frame content tests
///////////////////////////////////////////////////
function setUpAndTearDown() {
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
}
gTests.push({
desc: "normalize browser",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
info(chromeRoot + "browser_selection_frame_content.html");
yield addTab(chromeRoot + "browser_selection_frame_content.html");
yield waitForCondition(function () {
return !StartUI.isStartPageVisible;
}, 10000, 100);
gWindow = Browser.selectedTab.browser.contentWindow;
gFrame = gWindow.document.getElementById("frame1");
InputSourceHelper.isPrecise = false;
},
});
gTests.push({
desc: "iframe basic selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
gFrame.focus();
sendContextMenuClick(130, 95);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// active state
is(SelectionHelperUI.isActive, true, "selection active");
// check text selection
is(getTrimmedSelection(gFrame).toString(), "Alice", "selection test");
},
});
gTests.push({
desc: "iframe expand / collapse selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
gFrame.focus();
sendContextMenuClick(162, 180);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// active state
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gFrame).toString(), "Alice", "selection test");
let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
let touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 600, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gFrame).toString(), "Alice was beginning to get very tired of sitting by her sister on the bank",
"selection test");
touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 300, ypos);
touchdrag.end();
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gFrame).toString(), "Alice was beginning to get very", "selection test");
},
});
gTests.push({
desc: "scrolled iframe selection",
setUp: setUpAndTearDown,
run: function test() {
gFrame.focus();
let scrollPromise = waitForEvent(gFrame.contentDocument.defaultView, "scroll");
gFrame.contentDocument.defaultView.scrollBy(0, 200);
yield scrollPromise;
sendContextMenuClick(527, 188);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gFrame).toString(), "started", "selection test");
let promise = waitForEvent(document, "popupshown");
sendContextMenuClick(527, 188);
yield promise;
ok(promise && !(promise instanceof Error), "promise error");
ok(ContextMenuUI._menuPopup._visible, "is visible");
let menuItem = document.getElementById("context-copy");
ok(menuItem, "menu item exists");
ok(!menuItem.hidden, "menu item visible");
let popupPromise = waitForEvent(document, "popuphidden");
EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
yield popupPromise;
ok(popupPromise && !(popupPromise instanceof Error), "promise error");
// The wait is needed to give time to populate the clipboard.
let string = "";
yield waitForCondition(function () {
string = SpecialPowers.getClipboardData("text/unicode");
return string === "started";
});
is(string, "started", "copy text");
},
tearDown: function tearDown() {
emptyClipboard();
let scrollPromise = waitForEvent(gFrame.contentDocument.defaultView, "scroll");
gFrame.contentDocument.defaultView.scrollBy(0, -200);
yield scrollPromise;
clearSelection(gWindow);
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
},
});
gTests.push({
desc: "iframe within scrolled page selection",
setUp: setUpAndTearDown,
run: function test() {
gFrame.focus();
let scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, 200);
yield scrollPromise;
scrollPromise = waitForEvent(gFrame.contentDocument.defaultView, "scroll");
gFrame.contentDocument.defaultView.scrollBy(0, 200);
yield scrollPromise;
InputSourceHelper.isPrecise = false;
sendContextMenuClick(114, 91);
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(gFrame).toString(), "moment", "selection test");
},
tearDown: function tearDown() {
emptyClipboard();
clearSelection(gWindow);
clearSelection(gFrame);
let scrollPromise = waitForEvent(gFrame.contentDocument.defaultView, "scroll");
gFrame.contentDocument.defaultView.scrollBy(0, -200);
yield scrollPromise;
scrollPromise = waitForEvent(gWindow, "scroll");
gWindow.scrollBy(0, -200);
yield scrollPromise;
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
},
});
function test() {
if (isDebugBuild()) {
todo(false, "selection tests can't run in debug builds.");
return;
}
if (!isLandscapeMode()) {
todo(false, "browser_selection_tests need landscape mode to run.");
return;
}
requestLongerTimeout(3);
runTests();
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form action="texarea.html">
<div style="margin-left: 250px;">
<textarea wrap="on" style="width:800px; height:100px; overflow:scroll;" id="inputtext">
Alice was beginning to get very tired of sitting by her sister on the bank, and of having
nothing to do: once or twice she had peeped into the book her sister was reading, but it
had no pictures or conversations in it, `and what is the use of a book,' thought Alice
`without pictures or conversation?'
Either the well was very deep, or she fell very slowly, for she had plenty of time as she
went down to look about her and to wonder what was going to happen next. First, she tried
to look down and make out what she was coming to, but it was too dark to see anything;
then she looked at the sides of the well, and noticed that they were filled with cupboards
and book-shelves;(end)</textarea>
</div>
</body>
</html>

View File

@ -0,0 +1,158 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gWindow = null;
var gFrame = null;
const kMarkerOffsetY = 12;
const kCommonWaitMs = 5000;
const kCommonPollMs = 100;
///////////////////////////////////////////////////
// text area tests
///////////////////////////////////////////////////
function setUpAndTearDown() {
emptyClipboard();
if (gWindow)
clearSelection(gWindow);
if (gFrame)
clearSelection(gFrame);
yield waitForCondition(function () {
return !SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
yield hideContextUI();
}
gTests.push({
desc: "normalize browser",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
info(chromeRoot + "browser_selection_textarea.html");
yield addTab(chromeRoot + "browser_selection_textarea.html");
yield waitForCondition(function () {
return !StartUI.isStartPageVisible;
}, 10000, 100);
gWindow = Browser.selectedTab.browser.contentWindow;
InputSourceHelper.isPrecise = false;
},
});
gTests.push({
desc: "textarea basic selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
let textarea = gWindow.document.getElementById("inputtext");
textarea.focus();
let promise = waitForEvent(document, "popupshown");
sendContextMenuClick(355, 50);
yield promise;
checkContextUIMenuItemVisibility(["context-select",
"context-select-all"]);
let menuItem = document.getElementById("context-select");
ok(menuItem, "menu item exists");
ok(!menuItem.hidden, "menu item visible");
let popupPromise = waitForEvent(document, "popuphidden");
EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
yield popupPromise;
ok(popupPromise && !(popupPromise instanceof Error), "promise error");
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
// check text selection
is(getTrimmedSelection(textarea).toString(), "pictures", "selection test");
clearSelection(textarea);
},
});
gTests.push({
desc: "textarea complex drag selection",
setUp: setUpAndTearDown,
tearDown: setUpAndTearDown,
run: function test() {
// work around for buggy context menu display
yield waitForMs(100);
let textarea = gWindow.document.getElementById("inputtext");
let promise = waitForEvent(document, "popupshown");
sendContextMenuClick(355, 50);
yield promise;
checkContextUIMenuItemVisibility(["context-select",
"context-select-all"]);
let menuItem = document.getElementById("context-select");
ok(menuItem, "menu item exists");
ok(!menuItem.hidden, "menu item visible");
let popupPromise = waitForEvent(document, "popuphidden");
EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
yield popupPromise;
ok(popupPromise && !(popupPromise instanceof Error), "promise error");
yield waitForCondition(function () {
return SelectionHelperUI.isSelectionUIVisible;
}, kCommonWaitMs, kCommonPollMs);
is(SelectionHelperUI.isActive, true, "selection active");
is(getTrimmedSelection(textarea).toString(), "pictures", "selection test");
let xpos = SelectionHelperUI.endMark.xPos;
let ypos = SelectionHelperUI.endMark.yPos + 10;
var touchdrag = new TouchDragAndHold();
// end marker and off the text area to the right
yield touchdrag.start(gWindow, xpos, ypos, 1200, 400);
let textLength = getTrimmedSelection(textarea).toString().length;
yield waitForCondition(function () {
let newTextLength = getTrimmedSelection(textarea).toString().length;
if (textLength != newTextLength) {
textLength = newTextLength;
return false;
}
return true;
}, 45000, 1000);
touchdrag.end();
touchdrag = null;
yield waitForCondition(function () {
return !SelectionHelperUI.hasActiveDrag;
}, kCommonWaitMs, kCommonPollMs);
yield SelectionHelperUI.pingSelectionHandler();
let text = getTrimmedSelection(textarea).toString();
let end = text.substring(text.length - "(end)".length);
is(end, "(end)", "selection test");
},
});
function test() {
if (isDebugBuild()) {
todo(false, "selection tests can't run in debug builds.");
return;
}
if (!isLandscapeMode()) {
todo(false, "browser_selection_tests need landscape mode to run.");
return;
}
requestLongerTimeout(3);
runTests();
}

View File

@ -22,6 +22,11 @@ const kDefaultInterval = 50;
Metro ui helpers
=============================================================================*/
function isLandscapeMode()
{
return (MetroUtils.snappedState == Ci.nsIWinMetroUtils.fullScreenLandscape);
}
function checkContextUIMenuItemCount(aCount)
{
let visibleCount = 0;
@ -92,6 +97,40 @@ function showNotification()
});
}
function getSelection(aElement) {
if (!aElement)
return null;
// editable element
if (aElement instanceof Ci.nsIDOMNSEditableElement) {
return aElement.QueryInterface(Ci.nsIDOMNSEditableElement)
.editor.selection;
}
// document or window
if (aElement instanceof HTMLDocument || aElement instanceof Window) {
return aElement.getSelection();
}
// browser
return aElement.contentWindow.getSelection();
};
function getTrimmedSelection(aElement) {
let sel = getSelection(aElement);
if (!sel)
return "";
return sel.toString().trim();
}
/*
* clearSelection(aTarget) - clears the current selection in
* aTarget, shuts down the selection manager and purges all
* message manager events to insure a reset state for the ui.
*/
function clearSelection(aTarget) {
SelectionHelperUI.closeEditSession(true);
getSelection(aTarget).removeAllRanges();
purgeEventQueue();
}
/*=============================================================================
Asynchronous Metro ui helpers
=============================================================================*/
@ -107,6 +146,25 @@ function hideContextUI()
}
}
function showNavBar()
{
let promise = waitForEvent(Elements.tray, "transitionend");
if (!ContextUI.isVisible) {
ContextUI.displayNavbar();
return promise;
}
}
function fireAppBarDisplayEvent()
{
let promise = waitForEvent(Elements.tray, "transitionend");
let event = document.createEvent("Events");
event.initEvent("MozEdgeUIGesture", true, false);
gWindow.dispatchEvent(event);
purgeEventQueue();
return promise;
}
/*=============================================================================
Asynchronous test helpers
=============================================================================*/
@ -448,10 +506,104 @@ function sendContextMenuClickToElement(aWindow, aElement, aX, aY) {
1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
}
/*
* sendDoubleTap - simulates a double click or double tap.
*/
function sendDoubleTap(aWindow, aX, aY) {
EventUtils.synthesizeMouseAtPoint(aX, aY, {
clickCount: 1,
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
}, aWindow);
EventUtils.synthesizeMouseAtPoint(aX, aY, {
clickCount: 2,
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
}, aWindow);
}
function sendTap(aWindow, aX, aY) {
EventUtils.synthesizeMouseAtPoint(aX, aY, {
clickCount: 1,
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
}, aWindow);
}
/*
* sendTouchDrag - sends a touch series composed of a touchstart,
* touchmove, and touchend w3c event.
*/
function sendTouchDrag(aWindow, aStartX, aStartY, aEndX, aEndY) {
EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
EventUtils.synthesizeTouchAtPoint(aEndX, aEndY, { type: "touchmove" }, aWindow);
EventUtils.synthesizeTouchAtPoint(aEndX, aEndY, { type: "touchend" }, aWindow);
}
/*
* TouchDragAndHold - simulates a drag and hold sequence of events.
*/
function TouchDragAndHold() {
}
TouchDragAndHold.prototype = {
_timeoutStep: 2,
_numSteps: 50,
_debug: false,
callback: function callback() {
if (++this._step.steps >= this._numSteps) {
EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
{ type: "touchmove" }, this._win);
this._defer.resolve();
return;
}
this._currentPoint.xPos += this._step.x;
this._currentPoint.yPos += this._step.y;
if (this._debug) {
info("[" + this._step.steps + "] touchmove " + this._currentPoint.xPos + " x " + this._currentPoint.yPos);
}
EventUtils.synthesizeTouchAtPoint(this._currentPoint.xPos, this._currentPoint.yPos,
{ type: "touchmove" }, this._win);
let self = this;
setTimeout(function () { self.callback(); }, this._timeoutStep);
},
start: function start(aWindow, aStartX, aStartY, aEndX, aEndY) {
this._defer = Promise.defer();
this._win = aWindow;
this._endPoint = { xPos: aEndX, yPos: aEndY };
this._currentPoint = { xPos: aStartX, yPos: aStartY };
this._step = { steps: 0, x: (aEndX - aStartX) / this._numSteps, y: (aEndY - aStartY) / this._numSteps };
if (this._debug) {
info("[0] touchstart " + aStartX + " x " + aStartY);
}
EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
let self = this;
setTimeout(function () { self.callback(); }, this._timeoutStep);
return this._defer.promise;
},
end: function start() {
if (this._debug) {
info("[" + this._step.steps + "] touchend " + this._endPoint.xPos + " x " + this._endPoint.yPos);
}
EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
{ type: "touchend" }, this._win);
this._win = null;
},
};
/*=============================================================================
System utilities
=============================================================================*/
/*
* emptyClipboard - clear the windows clipbaord.
*/
function emptyClipboard() {
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
}
/*
* purgeEventQueue - purges the event queue on the calling thread.
* Pumps latent in-process message manager events awaiting delivery.

View File

@ -14,10 +14,10 @@
(end of paragraph)</p>
(in between paragraphs)
<p>(start of paragraph)
Alice was beginning to get very
tired of sitting by her sister on the bank, and of having nothing to do: once or twice she
had peeped into the book her sister was reading, but it had no pictures or conversations
in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'(break)<br>
Alice was beginning to get very tired of sitting by her sister on the bank, and of having
nothing to do: once or twice she had peeped into the book her sister was reading, but it
had no pictures or conversations in it, `and what is the use of a book,' thought Alice
`without pictures or conversation?'(break)<br>
(end of paragraph)</p>
<p><a id="link1" href="#hello">(start of paragraph)</a>

View File

@ -9,7 +9,7 @@
{"index":2,"title":"@getting_started@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/firefox/central/",
"iconUri":"chrome://branding/content/favicon32.png"
},
{"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/firefox/community/",
{"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/",
"iconUri":"chrome://branding/content/favicon32.png"
},
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

View File

@ -82,5 +82,8 @@ chrome.jar:
skin/images/scrubber-hdpi.png (images/scrubber-hdpi.png)
skin/images/selection-monocle.png (images/selection-monocle.png)
skin/images/appbar-icons.png (images/appbar-icons.png)
skin/images/pinned-hdpi.png (images/pinned-hdpi.png)
skin/images/tile-selected-check-hdpi.png (images/tile-selected-check-hdpi.png)
skin/images/overlay-back.png (images/overlay-back.png)
skin/images/overlay-plus.png (images/overlay-plus.png)

View File

@ -473,9 +473,22 @@ richgriditem .richgrid-item-content {
background: #fff;
}
richgriditem[selected] .richgrid-item-content {
richgriditem[selected] .richgrid-item-content::after {
content: "";
pointer-events: none;
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-image: url(chrome://browser/skin/images/tile-selected-check-hdpi.png);
background-origin: border-box;
background-position: right 0 top 0;
background-repeat: no-repeat;
/* scale the image whatever the dppx */
background-size: 35px 35px;
border: @metro_border_xthick@ solid @selected_color@;
padding: @metro_spacing_xxsmall@;
}
richgriditem .richgrid-icon-container {
@ -487,18 +500,38 @@ richgriditem .richgrid-icon-box {
background: #fff;
opacity: 1.0;
}
/* <sfoster> placeholder pinned-state indication, tracked as 854960 */
richgriditem[pinned] .richgrid-item-content:after {
content: "\2193";
text-align: center;
/* tile pinned-state indication */
richgriditem[pinned] .richgrid-item-content::before {
pointer-events:none;
content: "";
display: block;
position: absolute;
width: 16px;
width: 35px;
height: 35px;
right: 0;
left: auto;
top: 0;
outline: 1px solid rgb(255,153,0);
background-color: rgba(255,153,0,0.6);
color: rgb(153,51,0);
background-image: url(chrome://browser/skin/images/pinned-hdpi.png);
background-position: center;
background-repeat: no-repeat;
/* scale the image whatever the dppx */
background-size: 70px 70px;
}
/* Selected _and_ pinned tiles*/
richgriditem[selected][pinned] .richgrid-item-content::before {
background-position: right -@metro_border_xthick@ top -@metro_border_xthick@;
width: 70px;
height: 70px;
}
richgriditem[pinned]:-moz-locale-dir(rtl) .richgrid-item-content::before {
left: 0;
right: auto;
}
richgriditem[customColor] {
color: #f1f1f1;
}

View File

@ -31,6 +31,9 @@ public interface Actions {
/** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
public boolean eventReceived();
/** Stop listening for events. */
public void unregisterListener();
}
public interface RepeatedEventExpecter extends EventExpecter {

View File

@ -17,6 +17,7 @@ import android.content.Context;
import android.app.Instrumentation;
import android.database.Cursor;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.View;
import android.view.KeyEvent;
import android.util.Log;
@ -83,10 +84,12 @@ public class FennecNativeActions implements Actions {
String methodName = method.getName();
//Depending on the method, return a completely different type.
if(methodName.equals("toString")) {
return "wakeInvocationHandler";
return this.toString();
}
if(methodName.equals("equals")) {
return this == args[0];
return
args[0] == null ? false :
this.toString().equals(args[0].toString());
}
if(methodName.equals("clone")) {
return this;
@ -103,13 +106,20 @@ public class FennecNativeActions implements Actions {
class GeckoEventExpecter implements RepeatedEventExpecter {
private final String mGeckoEvent;
private final Object[] mRegistrationParams;
private Object[] mRegistrationParams;
private boolean mEventReceived;
private boolean mEventEverReceived;
private String mEventData;
private static final int MAX_WAIT_MS = 90000;
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
if (TextUtils.isEmpty(geckoEvent)) {
throw new IllegalArgumentException("geckoEvent must not be empty");
}
if (registrationParams == null || registrationParams.length == 0) {
throw new IllegalArgumentException("registrationParams must not be empty");
}
mGeckoEvent = geckoEvent;
mRegistrationParams = registrationParams;
}
@ -119,6 +129,9 @@ public class FennecNativeActions implements Actions {
}
private synchronized void blockForEvent(long millis, boolean failOnTimeout) {
if (mRegistrationParams == null) {
throw new IllegalStateException("listener not registered");
}
long startTime = SystemClock.uptimeMillis();
long endTime = 0;
while (! mEventReceived) {
@ -139,19 +152,15 @@ public class FennecNativeActions implements Actions {
return;
}
}
try {
mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams);
} catch (IllegalAccessException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
} catch (InvocationTargetException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
"unblocked on expecter for " + mGeckoEvent);
mEventReceived = false;
}
public synchronized void blockUntilClear(long millis) {
if (mRegistrationParams == null) {
throw new IllegalStateException("listener not registered");
}
if (millis <= 0) {
throw new IllegalArgumentException("millis must be > 0");
}
@ -189,13 +198,6 @@ public class FennecNativeActions implements Actions {
// we got a notify() before we could wait long enough, so we need to start over
startTime = endTime;
}
try {
mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams);
} catch (IllegalAccessException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
} catch (InvocationTargetException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
"unblocked on expecter for " + mGeckoEvent);
mEventReceived = false;
@ -211,6 +213,21 @@ public class FennecNativeActions implements Actions {
return mEventData;
}
public synchronized void unregisterListener() {
if (mRegistrationParams == null) {
throw new IllegalStateException("listener not registered");
}
try {
FennecNativeDriver.log(LogLevel.INFO, "EventExpecter: no longer listening for "+mGeckoEvent);
mUnregisterEventListener.invoke(mRobocopApi, mRegistrationParams);
mRegistrationParams = null;
} catch (IllegalAccessException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
} catch (InvocationTargetException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
}
public synchronized boolean eventReceived() {
return mEventEverReceived;
}
@ -283,13 +300,14 @@ public class FennecNativeActions implements Actions {
}
class PaintExpecter implements RepeatedEventExpecter {
private Object mLayerClient;
private boolean mPaintDone;
private boolean mListening;
private static final int MAX_WAIT_MS = 90000;
PaintExpecter() throws IllegalAccessException, InvocationTargetException {
Object proxy = Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListenerClass }, new DrawListenerProxy(this));
mSetDrawListener.invoke(mRobocopApi, proxy);
mListening = true;
}
void notifyOfEvent(Object[] args) {
@ -300,6 +318,9 @@ public class FennecNativeActions implements Actions {
}
private synchronized void blockForEvent(long millis, boolean failOnTimeout) {
if (!mListening) {
throw new IllegalStateException("draw listener not registered");
}
long startTime = SystemClock.uptimeMillis();
long endTime = 0;
while (!mPaintDone) {
@ -318,11 +339,6 @@ public class FennecNativeActions implements Actions {
return;
}
}
try {
mSetDrawListener.invoke(mRobocopApi, (Object)null);
} catch (Exception e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
}
public synchronized void blockForEvent() {
@ -344,6 +360,9 @@ public class FennecNativeActions implements Actions {
}
public synchronized void blockUntilClear(long millis) {
if (!mListening) {
throw new IllegalStateException("draw listener not registered");
}
if (millis <= 0) {
throw new IllegalArgumentException("millis must be > 0");
}
@ -381,7 +400,15 @@ public class FennecNativeActions implements Actions {
// we got a notify() before we could wait long enough, so we need to start over
startTime = endTime;
}
}
public synchronized void unregisterListener() {
if (!mListening) {
throw new IllegalStateException("listener not registered");
}
try {
FennecNativeDriver.log(LogLevel.INFO, "PaintExpecter: no longer listening for events");
mListening = false;
mSetDrawListener.invoke(mRobocopApi, (Object)null);
} catch (Exception e) {
FennecNativeDriver.log(LogLevel.ERROR, e);

View File

@ -1184,7 +1184,7 @@ public:
* Sanitize the document by resetting all input elements and forms that have
* autocomplete=off to their default values.
*/
virtual nsresult Sanitize() = 0;
virtual void Sanitize() = 0;
/**
* Enumerate all subdocuments.
@ -2091,7 +2091,7 @@ public:
nsISupports* aResult, mozilla::ErrorResult& rv);
// Touch event handlers already on nsINode
already_AddRefed<nsIDOMTouch>
CreateTouch(nsIDOMWindow* aView, nsISupports* aTarget,
CreateTouch(nsIDOMWindow* aView, mozilla::dom::EventTarget* aTarget,
int32_t aIdentifier, int32_t aPageX, int32_t aPageY,
int32_t aScreenX, int32_t aScreenY, int32_t aClientX,
int32_t aClientY, int32_t aRadiusX, int32_t aRadiusY,
@ -2103,7 +2103,7 @@ public:
already_AddRefed<nsIDOMTouchList>
CreateTouchList(const mozilla::dom::Sequence<nsRefPtr<nsIDOMTouch> >& aTouches);
nsHTMLDocument* AsHTMLDocument();
virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
private:
uint64_t mWarnedAbout;

View File

@ -1906,12 +1906,6 @@ nsDocument::Init()
return NS_OK;
}
nsHTMLDocument*
nsIDocument::AsHTMLDocument()
{
return IsHTML() ? static_cast<nsHTMLDocument*>(this) : nullptr;
}
void
nsIDocument::DeleteAllProperties()
{
@ -2947,11 +2941,11 @@ nsIDocument::GetActiveElement()
}
// No focused element anywhere in this document. Try to get the BODY.
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryObject(this);
nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
if (htmlDoc) {
// Because of IE compatibility, return null when html document doesn't have
// a body.
return static_cast<nsHTMLDocument*>(htmlDoc.get())->GetBody();
return htmlDoc->GetBody();
}
// If we couldn't get a BODY, return the root element.
@ -3121,7 +3115,7 @@ nsIDocument::ReleaseCapture() const
{
// only release the capture if the caller can access it. This prevents a
// page from stopping a scrollbar grab for example.
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(nsIPresShell::GetCapturingContent());
nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
if (node && nsContentUtils::CanCallerAccess(node)) {
nsIPresShell::SetCapturingContent(nullptr, 0);
}
@ -7451,7 +7445,7 @@ nsDocument::IsSafeToFlush() const
return shell->IsSafeToFlush();
}
nsresult
void
nsDocument::Sanitize()
{
// Sanitize the document by resetting all password fields and any form
@ -7463,24 +7457,16 @@ nsDocument::Sanitize()
// First locate all input elements, regardless of whether they are
// in a form, and reset the password and autocomplete=off elements.
nsCOMPtr<nsIDOMNodeList> nodes;
nsresult rv = GetElementsByTagName(NS_LITERAL_STRING("input"),
getter_AddRefs(nodes));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
uint32_t length = 0;
if (nodes)
nodes->GetLength(&length);
nsCOMPtr<nsIDOMNode> item;
nsCOMPtr<nsIContent> item;
nsAutoString value;
uint32_t i;
for (i = 0; i < length; ++i) {
nodes->Item(i, getter_AddRefs(item));
NS_ASSERTION(item, "null item in node list!");
uint32_t length = nodes->Length(true);
for (uint32_t i = 0; i < length; ++i) {
NS_ASSERTION(nodes->Item(i), "null item in node list!");
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(item);
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nodes->Item(i));
if (!input)
continue;
@ -7502,18 +7488,13 @@ nsDocument::Sanitize()
}
// Now locate all _form_ elements that have autocomplete=off and reset them
rv = GetElementsByTagName(NS_LITERAL_STRING("form"), getter_AddRefs(nodes));
NS_ENSURE_SUCCESS(rv, rv);
nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
length = 0;
if (nodes)
nodes->GetLength(&length);
length = nodes->Length(true);
for (uint32_t i = 0; i < length; ++i) {
NS_ASSERTION(nodes->Item(i), "null item in nodelist");
for (i = 0; i < length; ++i) {
nodes->Item(i, getter_AddRefs(item));
NS_ASSERTION(item, "null item in nodelist");
nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(item);
nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
if (!form)
continue;
@ -7521,8 +7502,6 @@ nsDocument::Sanitize()
if (value.LowerCaseEqualsLiteral("off"))
form->Reset();
}
return NS_OK;
}
struct SubDocEnumArgs
@ -9184,7 +9163,8 @@ nsDocument::CreateTouch(nsIDOMWindow* aView,
float aForce,
nsIDOMTouch** aRetVal)
{
*aRetVal = nsIDocument::CreateTouch(aView, aTarget, aIdentifier, aPageX,
nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
*aRetVal = nsIDocument::CreateTouch(aView, target, aIdentifier, aPageX,
aPageY, aScreenX, aScreenY, aClientX,
aClientY, aRadiusX, aRadiusY,
aRotationAngle, aForce).get();
@ -9193,7 +9173,7 @@ nsDocument::CreateTouch(nsIDOMWindow* aView,
already_AddRefed<nsIDOMTouch>
nsIDocument::CreateTouch(nsIDOMWindow* aView,
nsISupports* aTarget,
EventTarget* aTarget,
int32_t aIdentifier,
int32_t aPageX, int32_t aPageY,
int32_t aScreenX, int32_t aScreenY,
@ -9202,8 +9182,7 @@ nsIDocument::CreateTouch(nsIDOMWindow* aView,
float aRotationAngle,
float aForce)
{
nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
nsCOMPtr<nsIDOMTouch> touch = new Touch(target,
nsCOMPtr<nsIDOMTouch> touch = new Touch(aTarget,
aIdentifier,
aPageX, aPageY,
aScreenX, aScreenY,

View File

@ -817,7 +817,7 @@ public:
int32_t aNamespaceID,
nsIContent **aResult);
virtual NS_HIDDEN_(nsresult) Sanitize();
virtual NS_HIDDEN_(void) Sanitize();
virtual NS_HIDDEN_(void) EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
void *aData);

View File

@ -556,6 +556,19 @@ IsPluginEnabledForType(const nsCString& aMIMEType)
/// Member Functions
///
// Helper to queue a CheckPluginStopEvent for a OBJLC object
void
nsObjectLoadingContent::QueueCheckPluginStopEvent()
{
nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
mPendingCheckPluginStopEvent = event;
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
appShell->RunInStableState(event);
}
}
// Tedious syntax to create a plugin stream listener with checks and put it in
// mFinalListener
bool
@ -659,13 +672,7 @@ nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
// the event loop. If we get back to the event loop and the node
// has still not been added back to the document then we tear down the
// plugin
nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
mPendingCheckPluginStopEvent = event;
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
appShell->RunInStableState(event);
}
QueueCheckPluginStopEvent();
} else if (mType != eType_Image) {
// nsImageLoadingContent handles the image case.
// Reset state and clear pending events
@ -690,8 +697,8 @@ nsObjectLoadingContent::nsObjectLoadingContent()
nsObjectLoadingContent::~nsObjectLoadingContent()
{
// Should have been unbound from the tree at this point, and CheckPluginStopEvent
// keeps us alive
// Should have been unbound from the tree at this point, and
// CheckPluginStopEvent keeps us alive
if (mFrameLoader) {
NS_NOTREACHED("Should not be tearing down frame loaders at this point");
mFrameLoader->Destroy();
@ -820,16 +827,11 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
void
nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
{
if (!mInstanceOwner) {
return;
}
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
nsIDocument* ownerDoc = thisContent->OwnerDoc();
if (!ownerDoc->IsActive()) {
StopPluginInstance();
}
// If we have a plugin we want to queue an event to stop it unless we are
// moved into an active document before returning to the event loop.
if (mInstanceOwner)
QueueCheckPluginStopEvent();
}
// nsIRequestObserver
@ -995,15 +997,7 @@ nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
// CheckPluginStopEvent
if (mInstanceOwner) {
mInstanceOwner->SetFrame(nullptr);
nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
mPendingCheckPluginStopEvent = event;
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
appShell->RunInStableState(event);
} else {
NS_NOTREACHED("No app shell?");
}
QueueCheckPluginStopEvent();
}
return NS_OK;
}

View File

@ -344,6 +344,11 @@ class nsObjectLoadingContent : public nsImageLoadingContent
*/
ParameterUpdateFlags UpdateObjectParameters();
/**
* Queue a CheckPluginStopEvent and track it in mPendingCheckPluginStopEvent
*/
void QueueCheckPluginStopEvent();
void NotifyContentObjectWrapper();
/**

View File

@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_CanvasPattern_h
#define mozilla_dom_CanvasPattern_h
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/RefPtr.h"
#include "nsISupports.h"
#define NS_CANVASPATTERNAZURE_PRIVATE_IID \
{0xc9bacc25, 0x28da, 0x421e, {0x9a, 0x4b, 0xbb, 0xd6, 0x93, 0x05, 0x12, 0xbc}}
class nsIPrincipal;
namespace mozilla {
namespace gfx {
class SourceSurface;
}
namespace dom {
class CanvasPattern MOZ_FINAL : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERNAZURE_PRIVATE_IID)
NS_DECL_ISUPPORTS
enum RepeatMode
{
REPEAT,
REPEATX,
REPEATY,
NOREPEAT
};
CanvasPattern(mozilla::gfx::SourceSurface* aSurface,
RepeatMode aRepeat,
nsIPrincipal* principalForSecurityCheck,
bool forceWriteOnly,
bool CORSUsed)
: mSurface(aSurface)
, mRepeat(aRepeat)
, mPrincipal(principalForSecurityCheck)
, mForceWriteOnly(forceWriteOnly)
, mCORSUsed(CORSUsed)
{
}
JSObject* WrapObject(JSContext* aCx, JSObject* aScope)
{
return CanvasPatternBinding::Wrap(aCx, aScope, this);
}
mozilla::RefPtr<mozilla::gfx::SourceSurface> mSurface;
const RepeatMode mRepeat;
nsCOMPtr<nsIPrincipal> mPrincipal;
const bool mForceWriteOnly;
const bool mCORSUsed;
};
}
}
#endif // mozilla_dom_CanvasPattern_h

View File

@ -95,6 +95,7 @@
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/TextMetrics.h"
#ifdef USE_SKIA_GPU
#include "GLContext.h"
@ -118,9 +119,6 @@ using namespace mozilla::layers;
namespace mgfx = mozilla::gfx;
#define NS_TEXTMETRICSAZURE_PRIVATE_IID \
{0x9793f9e7, 0x9dc1, 0x4e9c, {0x81, 0xc8, 0xfc, 0xa7, 0x14, 0xf4, 0x30, 0x79}}
namespace mozilla {
namespace dom {
@ -412,44 +410,6 @@ NS_IMPL_RELEASE(CanvasPattern)
NS_INTERFACE_MAP_BEGIN(CanvasPattern)
NS_INTERFACE_MAP_ENTRY(mozilla::dom::CanvasPattern)
NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/**
** TextMetrics
**/
class TextMetrics : public nsIDOMTextMetrics
{
public:
TextMetrics(float w) : width(w) { }
virtual ~TextMetrics() { }
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICSAZURE_PRIVATE_IID)
NS_IMETHOD GetWidth(float* w)
{
*w = width;
return NS_OK;
}
NS_DECL_ISUPPORTS
private:
float width;
};
NS_DEFINE_STATIC_IID_ACCESSOR(TextMetrics, NS_TEXTMETRICSAZURE_PRIVATE_IID)
NS_IMPL_ADDREF(TextMetrics)
NS_IMPL_RELEASE(TextMetrics)
NS_INTERFACE_MAP_BEGIN(TextMetrics)
NS_INTERFACE_MAP_ENTRY(mozilla::dom::TextMetrics)
NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
@ -1401,7 +1361,7 @@ CanvasRenderingContext2D::CreateRadialGradient(double x0, double y0, double r0,
return grad.forget();
}
already_AddRefed<nsIDOMCanvasPattern>
already_AddRefed<CanvasPattern>
CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
const nsAString& repeat,
ErrorResult& error)
@ -2245,7 +2205,7 @@ CanvasRenderingContext2D::StrokeText(const nsAString& text, double x,
error = DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nullptr);
}
already_AddRefed<nsIDOMTextMetrics>
TextMetrics*
CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
ErrorResult& error)
{
@ -2256,9 +2216,7 @@ CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
return nullptr;
}
nsRefPtr<nsIDOMTextMetrics> textMetrics = new TextMetrics(width);
return textMetrics.forget();
return new TextMetrics(width);
}
/**
@ -3829,8 +3787,6 @@ CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager *aManager)
}
}
DOMCI_DATA(TextMetrics, mozilla::dom::TextMetrics)
DOMCI_DATA(CanvasGradient, mozilla::dom::CanvasGradient)
DOMCI_DATA(CanvasPattern, mozilla::dom::CanvasPattern)
DOMCI_DATA(CanvasRenderingContext2D, mozilla::dom::CanvasRenderingContext2D)

View File

@ -18,11 +18,10 @@
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/CanvasPattern.h"
#define NS_CANVASGRADIENTAZURE_PRIVATE_IID \
{0x28425a6a, 0x90e0, 0x4d42, {0x9c, 0x75, 0xff, 0x60, 0x09, 0xb3, 0x10, 0xa8}}
#define NS_CANVASPATTERNAZURE_PRIVATE_IID \
{0xc9bacc25, 0x28da, 0x421e, {0x9a, 0x4b, 0xbb, 0xd6, 0x93, 0x05, 0x12, 0xbc}}
class nsXULElement;
@ -33,6 +32,8 @@ class SourceSurface;
}
namespace dom {
class TextMetrics;
extern const mozilla::gfx::Float SIGMA_MAX;
template<typename T> class Optional;
@ -84,44 +85,6 @@ protected:
virtual ~CanvasGradient() {}
};
/**
** CanvasPattern
**/
class CanvasPattern MOZ_FINAL : public nsIDOMCanvasPattern
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERNAZURE_PRIVATE_IID)
enum RepeatMode
{
REPEAT,
REPEATX,
REPEATY,
NOREPEAT
};
CanvasPattern(mozilla::gfx::SourceSurface* aSurface,
RepeatMode aRepeat,
nsIPrincipal* principalForSecurityCheck,
bool forceWriteOnly,
bool CORSUsed)
: mSurface(aSurface)
, mRepeat(aRepeat)
, mPrincipal(principalForSecurityCheck)
, mForceWriteOnly(forceWriteOnly)
, mCORSUsed(CORSUsed)
{
}
NS_DECL_ISUPPORTS
mozilla::RefPtr<mozilla::gfx::SourceSurface> mSurface;
const RepeatMode mRepeat;
nsCOMPtr<nsIPrincipal> mPrincipal;
const bool mForceWriteOnly;
const bool mCORSUsed;
};
struct CanvasBidiProcessor;
class CanvasRenderingContext2DUserData;
@ -195,7 +158,7 @@ public:
already_AddRefed<nsIDOMCanvasGradient>
CreateRadialGradient(double x0, double y0, double r0, double x1, double y1,
double r1, mozilla::ErrorResult& aError);
already_AddRefed<nsIDOMCanvasPattern>
already_AddRefed<CanvasPattern>
CreatePattern(const HTMLImageOrCanvasOrVideoElement& element,
const nsAString& repeat, mozilla::ErrorResult& error);
@ -252,7 +215,7 @@ public:
void StrokeText(const nsAString& text, double x, double y,
const mozilla::dom::Optional<double>& maxWidth,
mozilla::ErrorResult& error);
already_AddRefed<nsIDOMTextMetrics>
TextMetrics*
MeasureText(const nsAString& rawText, mozilla::ErrorResult& error);
void DrawImage(const HTMLImageOrCanvasOrVideoElement& image,

View File

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_TextMetrics_h
#define mozilla_dom_TextMetrics_h
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
namespace mozilla {
namespace dom {
class TextMetrics MOZ_FINAL : public NonRefcountedDOMObject
{
public:
TextMetrics(float w) : width(w)
{
MOZ_COUNT_CTOR(TextMetrics);
}
~TextMetrics()
{
MOZ_COUNT_DTOR(TextMetrics);
}
float Width() const
{
return width;
}
JSObject* WrapObject(JSContext* aCx, JSObject* aScope, bool* aTookOwnership)
{
return TextMetricsBinding::Wrap(aCx, aScope, this, aTookOwnership);
}
private:
float width;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_TextMetrics_h

View File

@ -7,8 +7,10 @@
MODULE = 'content'
EXPORTS.mozilla.dom += [
'CanvasPattern.h',
'CanvasRenderingContext2D.h',
'CanvasUtils.h',
'ImageData.h',
'TextMetrics.h',
]

View File

@ -26,6 +26,7 @@ nsDOMDragEvent::nsDOMDragEvent(mozilla::dom::EventTarget* aOwner,
mEvent->refPoint.x = mEvent->refPoint.y = 0;
static_cast<nsMouseEvent*>(mEvent)->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
}
SetIsDOMBinding();
}
nsDOMDragEvent::~nsDOMDragEvent()
@ -74,27 +75,31 @@ nsDOMDragEvent::InitDragEvent(const nsAString & aType,
NS_IMETHODIMP
nsDOMDragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
{
NS_IF_ADDREF(*aDataTransfer = GetDataTransfer());
return NS_OK;
}
nsIDOMDataTransfer*
nsDOMDragEvent::GetDataTransfer()
{
// the dataTransfer field of the event caches the DataTransfer associated
// with the drag. It is initialized when an attempt is made to retrieve it
// rather that when the event is created to avoid duplicating the data when
// no listener ever uses it.
*aDataTransfer = nullptr;
if (!mEvent || mEvent->eventStructType != NS_DRAG_EVENT) {
NS_WARNING("Tried to get dataTransfer from non-drag event!");
return NS_OK;
return nullptr;
}
nsDragEvent* dragEvent = static_cast<nsDragEvent*>(mEvent);
// for synthetic events, just use the supplied data transfer object even if null
if (!mEventIsInternal) {
nsresult rv = nsContentUtils::SetDataTransferInEvent(dragEvent);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, nullptr);
}
NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer);
return NS_OK;
return dragEvent->dataTransfer;
}
nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,

View File

@ -8,6 +8,7 @@
#include "nsIDOMDragEvent.h"
#include "nsDOMMouseEvent.h"
#include "mozilla/dom/DragEventBinding.h"
class nsEvent;
@ -24,6 +25,30 @@ public:
NS_DECL_NSIDOMDRAGEVENT
NS_FORWARD_TO_NSDOMMOUSEEVENT
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope)
{
return mozilla::dom::DragEventBinding::Wrap(aCx, aScope, this);
}
nsIDOMDataTransfer* GetDataTransfer();
void InitDragEvent(const nsAString& aType,
bool aCanBubble, bool aCancelable,
nsIDOMWindow* aView, int32_t aDetail,
int32_t aScreenX, int32_t aScreenY,
int32_t aClientX, int32_t aClientY,
bool aCtrlKey, bool aAltKey, bool aShiftKey,
bool aMetaKey, uint16_t aButton,
mozilla::dom::EventTarget* aRelatedTarget,
nsIDOMDataTransfer* aDataTransfer,
mozilla::ErrorResult& aRv)
{
aRv = InitDragEvent(aType, aCanBubble, aCancelable,
aView, aDetail, aScreenX, aScreenY, aClientX, aClientY,
aCtrlKey, aAltKey, aShiftKey, aMetaKey, aButton,
aRelatedTarget, aDataTransfer);
}
};
nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,

View File

@ -11,6 +11,7 @@
#include "nsIDOMHTMLAnchorElement.h"
#include "nsILink.h"
#include "Link.h"
#include "base/compiler_specific.h"
namespace mozilla {
namespace dom {
@ -26,7 +27,7 @@ public:
HTMLAnchorElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
, Link(this)
, ALLOW_THIS_IN_INITIALIZER_LIST(Link(this))
{
SetIsDOMBinding();
}

View File

@ -6,6 +6,7 @@
#include "mozilla/dom/HTMLAreaElement.h"
#include "mozilla/dom/HTMLAreaElementBinding.h"
#include "base/compiler_specific.h"
NS_IMPL_NS_NEW_HTML_ELEMENT(Area)
@ -14,7 +15,7 @@ namespace dom {
HTMLAreaElement::HTMLAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo),
Link(this)
ALLOW_THIS_IN_INITIALIZER_LIST(Link(this))
{
SetIsDOMBinding();
}

View File

@ -1225,11 +1225,9 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
aResultValue = static_cast<double>(milliseconds);
return true;
default:
MOZ_ASSERT(false, "Unrecognized input type");
return false;
}
MOZ_NOT_REACHED();
return false;
}
double
@ -1429,7 +1427,7 @@ HTMLInputElement::ConvertNumberToString(double aValue,
return true;
}
default:
MOZ_NOT_REACHED();
MOZ_ASSERT(false, "Unrecognized input type");
return false;
}
}
@ -1488,7 +1486,7 @@ HTMLInputElement::GetValueAsDate(JSContext* aCx, ErrorResult& aRv)
}
}
MOZ_NOT_REACHED();
MOZ_ASSERT(false, "Unrecognized input type");
aRv.Throw(NS_ERROR_UNEXPECTED);
return JS::NullValue();
}
@ -6076,7 +6074,7 @@ HTMLInputElement::GetStepScaleFactor() const
case NS_FORM_INPUT_TIME:
return kStepScaleFactorTime;
default:
MOZ_NOT_REACHED();
MOZ_ASSERT(false, "Unrecognized input type");
return MOZ_DOUBLE_NaN();
}
}
@ -6094,7 +6092,7 @@ HTMLInputElement::GetDefaultStep() const
case NS_FORM_INPUT_TIME:
return kDefaultStepTime;
default:
MOZ_NOT_REACHED();
MOZ_ASSERT(false, "Unrecognized input type");
return MOZ_DOUBLE_NaN();
}
}

View File

@ -6,6 +6,7 @@
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLLinkElementBinding.h"
#include "base/compiler_specific.h"
#include "nsGenericHTMLElement.h"
#include "nsILink.h"
#include "nsGkAtoms.h"
@ -31,7 +32,7 @@ namespace dom {
HTMLLinkElement::HTMLLinkElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo),
Link(this)
ALLOW_THIS_IN_INITIALIZER_LIST(Link(this))
{
SetIsDOMBinding();
}

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/HTMLOptionElement.h"
#include "mozilla/dom/HTMLSelectElementBinding.h"
#include "mozilla/Util.h"
#include "base/compiler_specific.h"
#include "nsContentCreatorFunctions.h"
#include "nsError.h"
#include "nsEventDispatcher.h"
@ -104,7 +105,7 @@ SafeOptionListMutation::~SafeOptionListMutation()
HTMLSelectElement::HTMLSelectElement(already_AddRefed<nsINodeInfo> aNodeInfo,
FromParser aFromParser)
: nsGenericHTMLFormElement(aNodeInfo),
mOptions(new HTMLOptionsCollection(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(mOptions(new HTMLOptionsCollection(this))),
mIsDoneAddingChildren(!aFromParser),
mDisabledChanged(false),
mMutating(false),

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/HTMLTextAreaElementBinding.h"
#include "mozilla/Util.h"
#include "base/compiler_specific.h"
#include "nsIControllers.h"
#include "nsFocusManager.h"
@ -59,7 +60,7 @@ HTMLTextAreaElement::HTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo
mDisabledChanged(false),
mCanShowInvalidUI(true),
mCanShowValidUI(true),
mState(this)
ALLOW_THIS_IN_INITIALIZER_LIST(mState(this))
{
AddMutationObserver(this);

View File

@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = gkconhtmlcon_s
LIBXUL_LIBRARY = 1
FAIL_ON_WARNINGS = 1
CPPSRCS = \
HTMLPropertiesCollection.cpp \

View File

@ -255,6 +255,8 @@ public:
return nsIDocument::GetLocation();
}
virtual nsHTMLDocument* AsHTMLDocument() { return this; }
protected:
nsresult GetBodySize(int32_t* aWidth,
int32_t* aHeight);

View File

@ -6,8 +6,6 @@ function check_mp4(v, enabled) {
check("video/mp4", "maybe");
check("audio/mp4", "maybe");
check("audio/mpeg", "maybe");
check("audio/mp3", "maybe");
check("audio/x-m4a", "maybe");
// Not the MIME type that other browsers respond to, so we won't either.
@ -30,14 +28,24 @@ function check_mp4(v, enabled) {
check("video/mp4; codecs=\"avc1.64001E\"", "probably");
check("video/mp4; codecs=\"avc1.64001F\"", "probably");
check("audio/mpeg; codecs=\"mp3\"", "probably");
check("audio/mpeg; codecs=mp3", "probably");
check("audio/mp3; codecs=\"mp3\"", "probably");
check("audio/mp3; codecs=mp3", "probably");
check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
check("audio/mp4; codecs=mp4a.40.2", "probably");
check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
check("audio/x-m4a; codecs=mp4a.40.2", "probably");
}
function check_mp3(v, enabled) {
function check(type, expected) {
var ex = enabled ? expected : "";
is(v.canPlayType(type), ex, type + "='" + ex + "'");
}
check("audio/mpeg", "maybe");
check("audio/mp3", "maybe");
check("audio/mpeg; codecs=\"mp3\"", "probably");
check("audio/mpeg; codecs=mp3", "probably");
check("audio/mp3; codecs=\"mp3\"", "probably");
check("audio/mp3; codecs=mp3", "probably");
}

View File

@ -26,6 +26,12 @@ function IsWindowsVistaOrLater() {
return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
}
function IsWindows7() {
var re = /Windows NT (\d.\d)/;
var winver = navigator.userAgent.match(re);
return winver && winver.length == 2 && parseFloat(winver[1]) == 6.1;
}
function getMediaPref(name) {
// Can't use SpecialPowers.getBoolPref because it throws when pref isn't
// present, and for example on non-Windows systems the WMF prefs won't be
@ -48,6 +54,14 @@ var haveMp4 = (getMediaPref("windows-media-foundation.enabled") && IsWindowsVist
check_mp4(document.getElementById('v'), haveMp4);
if (!IsWindows7()) {
// Don't check MP3 support on Windows 7. MP3 is disabled on Win7SP0 (bug 852915),
// and there's no easy way from JS to distinguish which service pack is installed
// on a users system, so we just won't test it. We'll get MP3 support on Win7SP0
// via DirectShow once bug 861693 lands.
check_mp3(document.getElementById('v'), haveMp4);
}
mediaTestCleanup();
</script>
</pre>

View File

@ -21,6 +21,27 @@ MediaDecoderStateMachine* WMFDecoder::CreateStateMachine()
return new MediaDecoderStateMachine(this, new WMFReader(this));
}
/* static */
bool
WMFDecoder::IsMP3Supported()
{
if (!MediaDecoder::IsWMFEnabled()) {
return false;
}
if (WinUtils::GetWindowsVersion() != WinUtils::WIN7_VERSION) {
return true;
}
// We're on Windows 7. MP3 support is disabled if no service pack
// is installed, as it's crashy on Win7 SP0.
UINT spMajorVer = 0, spMinorVer = 0;
if (!WinUtils::GetWindowsServicePackVersion(spMajorVer, spMinorVer)) {
// Um... We can't determine the service pack version... Just block
// MP3 as a precaution...
return false;
}
return spMajorVer != 0;
}
bool
WMFDecoder::GetSupportedCodecs(const nsACString& aType,
char const *const ** aCodecList)
@ -30,13 +51,15 @@ WMFDecoder::GetSupportedCodecs(const nsACString& aType,
return false;
// Assume that if LoadDLLs() didn't fail, we can playback the types that
// we know should be supported on Windows 7+ using WMF.
// we know should be supported by Windows Media Foundation.
static char const *const mp3AudioCodecs[] = {
"mp3",
nullptr
};
if (aType.EqualsASCII("audio/mpeg") ||
aType.EqualsASCII("audio/mp3")) {
if ((aType.EqualsASCII("audio/mpeg") || aType.EqualsASCII("audio/mp3")) &&
IsMP3Supported()) {
// Note: We block MP3 playback on Window 7 SP0 since it seems to crash
// in some circumstances.
if (aCodecList) {
*aCodecList = mp3AudioCodecs;
}

View File

@ -42,6 +42,10 @@ public:
// Returns true if the WMF backend is preffed on, and we're running on a
// version of Windows which is likely to support WMF.
static bool IsEnabled();
// Returns true if MP3 decoding is enabled on this system. We block
// MP3 playback on Windows 7 SP0, since it's crashy on that platform.
static bool IsMP3Supported();
};
} // namespace mozilla

View File

@ -388,6 +388,28 @@ WMFReader::ConfigureVideoDecoder()
return S_OK;
}
static void
GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs)
{
MOZ_ASSERT(aCodecs);
MOZ_ASSERT(aNumCodecs);
if (WMFDecoder::IsMP3Supported()) {
static const GUID codecs[] = {
MFAudioFormat_AAC,
MFAudioFormat_MP3
};
*aCodecs = codecs;
*aNumCodecs = NS_ARRAY_LENGTH(codecs);
} else {
static const GUID codecs[] = {
MFAudioFormat_AAC
};
*aCodecs = codecs;
*aNumCodecs = NS_ARRAY_LENGTH(codecs);
}
}
HRESULT
WMFReader::ConfigureAudioDecoder()
{
@ -399,15 +421,15 @@ WMFReader::ConfigureAudioDecoder()
return S_OK;
}
static const GUID MP4AudioTypes[] = {
MFAudioFormat_AAC,
MFAudioFormat_MP3
};
const GUID* codecs;
uint32_t numCodecs = 0;
GetSupportedAudioCodecs(&codecs, &numCodecs);
HRESULT hr = ConfigureSourceReaderStream(mSourceReader,
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
MFAudioFormat_Float,
MP4AudioTypes,
NS_ARRAY_LENGTH(MP4AudioTypes));
codecs,
numCodecs);
if (FAILED(hr)) {
NS_WARNING("Failed to configure WMF Audio decoder for PCM output");
return hr;

View File

@ -11416,9 +11416,17 @@ nsDocShell::EnsureScriptEnvironment()
uint32_t chromeFlags;
browserChrome->GetChromeFlags(&chromeFlags);
bool isModalContentWindow =
(chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) &&
!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
bool isModalContentWindow = (mItemType == typeContent) &&
(chromeFlags & nsIWebBrowserChrome::CHROME_MODAL);
// There can be various other content docshells associated with the
// top-level window, like sidebars. Make sure that we only create an
// nsGlobalModalWindow for the primary content shell.
if (isModalContentWindow) {
nsCOMPtr<nsIDocShellTreeItem> primaryItem;
nsresult rv = mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
NS_ENSURE_SUCCESS(rv, rv);
isModalContentWindow = (primaryItem == this);
}
// If our window is modal and we're not opened as chrome, make
// this window a modal content window.

View File

@ -731,10 +731,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(CanvasGradient, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(CanvasPattern, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(TextMetrics, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozCanvasPrintState, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@ -2028,14 +2024,6 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMCanvasGradient)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(CanvasPattern, nsIDOMCanvasPattern)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMCanvasPattern)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(TextMetrics, nsIDOMTextMetrics)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMTextMetrics)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MozCanvasPrintState, nsIDOMMozCanvasPrintState)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCanvasPrintState)
DOM_CLASSINFO_MAP_END

View File

@ -120,8 +120,6 @@ DOMCI_CLASS(SVGZoomEvent)
// Canvas
DOMCI_CLASS(CanvasGradient)
DOMCI_CLASS(CanvasPattern)
DOMCI_CLASS(TextMetrics)
DOMCI_CLASS(MozCanvasPrintState)
// WindowUtils

View File

@ -1469,6 +1469,12 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
return NS_ERROR_FAILURE;
}
bool isProxy = js::IsProxy(aObj);
JSObject* expandoObject;
if (isProxy) {
expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
}
JSAutoCompartment newAc(aCx, newParent);
// First we clone the reflector. We get a copy of its properties and clone its
@ -1501,15 +1507,20 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
{
AutoCloneDOMObjectSlotGuard cloneGuard(aObj, newobj);
JSObject* copyFrom = isProxy ? expandoObject : aObj;
if (copyFrom) {
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr,
newParent);
if (!propertyHolder) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, aObj)) {
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
return NS_ERROR_FAILURE;
}
} else {
propertyHolder = nullptr;
}
// Expandos from other compartments are attached to the target JS object.
// Copy them over, and let the old ones die a natural death.
@ -1557,9 +1568,19 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
cache->SetPreservingWrapper(false);
cache->SetWrapper(aObj);
cache->SetPreservingWrapper(preserving);
if (!JS_CopyPropertiesFrom(aCx, aObj, propertyHolder)) {
if (propertyHolder) {
JSObject* copyTo;
if (isProxy) {
copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
} else {
copyTo = aObj;
}
if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
MOZ_CRASH();
}
}
nsObjectLoadingContent* htmlobject;
nsresult rv = UnwrapObject<HTMLObjectElement>(aCx, aObj, htmlobject);

View File

@ -636,6 +636,41 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope, T* value,
return JS_WrapValue(cx, vp);
}
// Create a JSObject wrapping "value", for cases when "value" is a
// non-wrapper-cached owned object using WebIDL bindings. "value" must implement a
// WrapObject() method taking a JSContext, a scope, and a boolean outparam that
// is true if the JSObject took ownership
template <class T>
inline bool
WrapNewBindingNonWrapperCachedOwnedObject(JSContext* cx, JSObject* scope,
nsAutoPtr<T>& value, JS::Value* vp)
{
// We try to wrap in the compartment of the underlying object of "scope"
JSObject* obj;
{
// scope for the JSAutoCompartment so that we restore the compartment
// before we call JS_WrapValue.
Maybe<JSAutoCompartment> ac;
if (js::IsWrapper(scope)) {
scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
if (!scope)
return false;
ac.construct(cx, scope);
}
bool tookOwnership = false;
obj = value->WrapObject(cx, scope, &tookOwnership);
if (tookOwnership) {
value.forget();
}
}
// We can end up here in all sorts of compartments, per above. Make
// sure to JS_WrapValue!
*vp = JS::ObjectValue(*obj);
return JS_WrapValue(cx, vp);
}
// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
template <template <typename> class SmartPtr, typename T>
inline bool

View File

@ -143,12 +143,16 @@ DOMInterfaces = {
'headerFile': 'BatteryManager.h'
},
'CanvasPattern': {
'wrapperCache': False,
},
'CanvasRenderingContext2D': {
'implicitJSContext': [
'createImageData', 'getImageData', 'strokeStyle',
'fillStyle', 'mozDash'
],
'resultNotAddRefed': [ 'canvas' ],
'resultNotAddRefed': [ 'canvas', 'measureText' ],
'binaryNames': {
'mozImageSmoothingEnabled': 'imageSmoothingEnabled',
'mozFillRule': 'fillRule'
@ -268,6 +272,10 @@ DOMInterfaces = {
'nativeType': 'nsDOMTokenList',
},
'DragEvent': {
'nativeType': 'nsDOMDragEvent',
},
'DummyInterface': {
'skipGen': True,
'register': False,
@ -988,6 +996,11 @@ DOMInterfaces = {
'implicitJSContext': [ 'encode' ],
}],
'TextMetrics': {
'wrapperCache': False,
'nativeOwnership': 'owned',
},
'TimeRanges': {
'wrapperCache': False
},
@ -1469,7 +1482,6 @@ addExternalHTMLElement('HTMLFormElement')
addExternalIface('ActivityOptions', nativeType='nsIDOMMozActivityOptions',
headerFile='nsIDOMActivityOptions.h')
addExternalIface('CanvasGradient', headerFile='nsIDOMCanvasRenderingContext2D.h')
addExternalIface('CanvasPattern', headerFile='nsIDOMCanvasRenderingContext2D.h')
addExternalIface('Counter')
addExternalIface('CSSRule')
addExternalIface('DeviceAcceleration', headerFile='nsIDOMDeviceMotionEvent.h', notflattened=True)
@ -1516,7 +1528,6 @@ addExternalIface('SVGAnimatedNumber')
addExternalIface('SVGAnimatedString')
addExternalIface('SVGLength')
addExternalIface('SVGNumber')
addExternalIface('TextMetrics', headerFile='nsIDOMCanvasRenderingContext2D.h')
addExternalIface('Touch', headerFile='nsIDOMTouchEvent.h')
addExternalIface('TouchList', headerFile='nsIDOMTouchEvent.h')
addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
@ -1528,4 +1539,4 @@ addExternalIface('XPathResult', nativeType='nsISupports')
addExternalIface('XPathExpression')
addExternalIface('XPathNSResolver')
addExternalIface('XULCommandDispatcher')
addExternalIface('DataTransfer')
addExternalIface('DataTransfer', notflattened=True)

View File

@ -1866,6 +1866,7 @@ def CreateBindingJSObject(descriptor, parent):
create += """ // Make sure the native objects inherit from NonRefcountedDOMObject so that we
// log their ctor and dtor.
MustInheritFromNonRefcountedDOMObject(aObject);
*aTookOwnership = true;
"""
return create % parent
@ -2004,6 +2005,8 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
assert descriptor.interface.hasInterfacePrototypeObject()
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
Argument(descriptor.nativeType + '*', 'aObject')]
if descriptor.nativeOwnership == 'owned':
args.append(Argument('bool*', 'aTookOwnership'))
CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
self.properties = properties
@ -3542,10 +3545,14 @@ if (!returnArray) {
failed = None
elif not descriptor.interface.isExternal() and not descriptor.skipGen:
if descriptor.wrapperCache:
assert descriptor.nativeOwnership != 'owned'
wrapMethod = "WrapNewBindingObject"
else:
if not isCreator:
raise MethodNotCreatorError(descriptor.interface.identifier.name)
if descriptor.nativeOwnership == 'owned':
wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
else:
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
if not descriptor.hasXPConnectImpls:
@ -3779,6 +3786,9 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
returnType.unroll().inner.identifier.name).nativeType)
if resultAlreadyAddRefed:
result = CGTemplatedType("nsRefPtr", result)
elif descriptorProvider.getDescriptor(
returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
result = CGTemplatedType("nsAutoPtr", result)
else:
result = CGWrapper(result, post="*")
return result, False
@ -4005,6 +4015,9 @@ if (global.Failed()) {
# We better be returning addrefed things!
assert(isResultAlreadyAddRefed(self.descriptor,
self.extendedAttributes) or
# Creators can return raw pointers to owned objects
(self.returnType.isGeckoInterface() and
self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned') or
# Workers use raw pointers for new-object return
# values or something
self.descriptor.workers)
@ -6620,6 +6633,11 @@ class CGDescriptor(CGThing):
assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
if descriptor.nativeOwnership == 'owned' and (
descriptor.interface.hasChildInterfaces() or
descriptor.interface.parent):
raise TypeError("Owned interface cannot have a parent or children")
self._deps = descriptor.interface.getDeps()
cgThings = []
@ -7984,9 +8002,7 @@ ${nativeType}::${nativeType}()
${nativeType}::~${nativeType}()
{
}
"""
if self.descriptor.wrapperCache:
classImpl += """
JSObject*
${nativeType}::WrapObject(JSContext* aCx, JSObject* aScope)
{
@ -7994,16 +8010,6 @@ ${nativeType}::WrapObject(JSContext* aCx, JSObject* aScope)
}
"""
else:
classImpl += """
JSObject*
${nativeType}::WrapObject(JSContext* aCx, JSObject* aScope)
{
return ${ifaceName}Binding::Wrap(aCx, aScope, this);
}
"""
return string.Template(classImpl).substitute(
{ "ifaceName": self.descriptor.name,
"nativeType": self.descriptor.nativeType.split('::')[-1] }

View File

@ -47,6 +47,16 @@ struct SetListBaseInformation
SetListBaseInformation gSetListBaseInformation;
// static
JSObject*
DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
{
JSObject* expando = GetExpandoObject(obj);
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
scope->RemoveDOMExpandoObject(obj);
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
return expando;
}
// static
JSObject*

View File

@ -54,6 +54,7 @@ public:
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
return v.isUndefined() ? NULL : v.toObjectOrNull();
}
static JSObject* GetAndClearExpandoObject(JSObject* obj);
static JSObject* EnsureExpandoObject(JSContext* cx, JSObject* obj);
const DOMClass& mClass;

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
function boom()
{
var frameDoc = document.getElementById("f").contentDocument;
frameDoc.adoptNode(document.createElement("select"));
}
</script>
</head>
<body onload="boom();">
<iframe id="f"></iframe>
</body>
</html>

View File

@ -5,3 +5,4 @@ load 832899.html
load 860591.html
load 860551.html
load 862610.html
load 862092.html

View File

@ -72,6 +72,7 @@ MOCHITEST_FILES := \
test_queryInterface.html \
test_exceptionThrowing.html \
test_bug852846.html \
test_bug862092.html \
$(NULL)
MOCHITEST_CHROME_FILES = \

View File

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=862092
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 862092</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 862092 **/
SimpleTest.waitForExplicitFinish();
function runTest()
{
var frameDoc = document.getElementById("f").contentDocument;
var a = document.createElement("select");
a.expando = "test";
a = frameDoc.adoptNode(a)
is(a.expando, "test", "adoptNode needs to preserve expandos");
SimpleTest.finish();
}
</script>
</head>
<body onload="runTest();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862092">Mozilla Bug 862092</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe id="f"></iframe>
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -11,17 +11,6 @@ interface nsIDOMCanvasGradient : nsISupports
void addColorStop(in float offset, in DOMString color);
};
[scriptable, uuid(21dea65c-5c08-4eb1-ac82-81fe95be77b8)]
interface nsIDOMCanvasPattern : nsISupports
{
};
[scriptable, uuid(2d01715c-ec7d-424a-ab85-e0fd70c8665c)]
interface nsIDOMTextMetrics : nsISupports
{
readonly attribute float width;
};
/**
* This interface remains only for the constants, for a context, use the
* WebIDL/Paris bindings instead (CanvasRenderingContext2D.webidl).

View File

@ -55,6 +55,14 @@ MOCHITEST_FILES = \
# Disable this test until bug 795711 is fixed.
# test_network_events.html \
# Don't run modal dialog tests on mobile.
ifneq ($(OS_TARGET),Android)
MOCHITEST_FILES += \
test_showModalDialog.html \
file_showModalDialog.html \
$(NULL)
endif
MOCHITEST_CHROME_FILES = \
test_innerScreen.xul \
test_offsets.xul \

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<script>
ok = window.opener.ok;
is = window.opener.is;
SpecialPowers = window.opener.SpecialPowers;
function go() {
is(SpecialPowers.Cu.getClassName(window, /* aUnwrap = */ true), "ModalContentWindow", "We are modal");
var iwin = document.getElementById('ifr').contentWindow;
is(SpecialPowers.Cu.getClassName(iwin, /* aUnwrap = */ true), "Window", "Descendant frames should not be modal");
window.close();
}
</script>
</head>
<body onload="go();">
<iframe id="ifr"></iframe>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=862918
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 862918</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
// This will be fixed in bug 860941.
SimpleTest.expectAssertions(0, 1);
/** Test for window.showModalDialog. **/
window.showModalDialog('file_showModalDialog.html');
// Blame mArguments assertion on this test.
SpecialPowers.gc();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862918">Mozilla Bug 862918</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -12,9 +12,7 @@
*/
interface CanvasGradient;
interface CanvasPattern;
interface HitRegionOptions;
interface TextMetrics;
interface Window;
enum CanvasWindingRule { "nonzero", "evenodd" };
@ -57,7 +55,7 @@ interface CanvasRenderingContext2D {
CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
[Throws]
CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
[Throws]
[Creator, Throws]
CanvasPattern createPattern((HTMLImageElement or HTMLCanvasElement or HTMLVideoElement) image, [TreatNullAs=EmptyString] DOMString repetition);
// shadows
@ -101,7 +99,7 @@ interface CanvasRenderingContext2D {
void fillText(DOMString text, double x, double y, optional double maxWidth);
[Throws, LenientFloat]
void strokeText(DOMString text, double x, double y, optional double maxWidth);
[Throws]
[Creator, Throws]
TextMetrics measureText(DOMString text);
// drawing images
@ -264,3 +262,34 @@ interface CanvasPathMethods {
void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false);
// NOT IMPLEMENTED [LenientFloat] void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, boolean anticlockwise);
};
interface CanvasPattern {
// opaque object
// void setTransform(SVGMatrix transform);
};
interface TextMetrics {
// x-direction
readonly attribute double width; // advance width
/*
* NOT IMPLEMENTED YET
readonly attribute double actualBoundingBoxLeft;
readonly attribute double actualBoundingBoxRight;
// y-direction
readonly attribute double fontBoundingBoxAscent;
readonly attribute double fontBoundingBoxDescent;
readonly attribute double actualBoundingBoxAscent;
readonly attribute double actualBoundingBoxDescent;
readonly attribute double emHeightAscent;
readonly attribute double emHeightDescent;
readonly attribute double hangingBaseline;
readonly attribute double alphabeticBaseline;
readonly attribute double ideographicBaseline;
*/
};

View File

@ -292,11 +292,7 @@ partial interface Document {
// from our xpidl for now.
[Creator, Func="nsGenericHTMLElement::TouchEventsEnabled"]
Touch createTouch(optional Window? view = null,
// Nasty hack, because we can't do EventTarget arguments yet
// (they would need to be non-castable, but trying to do
// XPConnect unwrapping with nsDOMEventTargetHelper fails).
// optional EventTarget? target = null,
optional nsISupports? target = null,
optional EventTarget? target = null,
optional long identifier = 0,
optional long pageX = 0,
optional long pageY = 0,

View File

@ -0,0 +1,31 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
interface WindowProxy;
interface DataTransfer;
interface DragEvent : MouseEvent
{
readonly attribute DataTransfer? dataTransfer;
[Throws]
void initDragEvent(DOMString type,
boolean canBubble,
boolean cancelable,
WindowProxy? aView,
long aDetail,
long aScreenX,
long aScreenY,
long aClientX,
long aClientY,
boolean aCtrlKey,
boolean aAltKey,
boolean aShiftKey,
boolean aMetaKey,
unsigned short aButton,
EventTarget? aRelatedTarget,
DataTransfer? aDataTransfer);
};

View File

@ -58,6 +58,7 @@ webidl_files = \
DOMStringMap.webidl \
DOMTokenList.webidl \
DOMTransaction.webidl \
DragEvent.webidl \
DummyBinding.webidl \
DynamicsCompressorNode.webidl \
Element.webidl \

View File

@ -1473,6 +1473,7 @@ void AsyncPanZoomController::SetState(PanZoomState aState) {
}
void AsyncPanZoomController::TimeoutTouchListeners() {
mTouchListenerTimeoutTask = nullptr;
ContentReceivedTouch(false);
}

View File

@ -174,6 +174,7 @@ nsresult nsTextToSubURI::convertURItoUnicode(const nsAFlatCString &aCharset,
rv = charsetConverterManager->GetUnicodeDecoder(aCharset.get(),
getter_AddRefs(unicodeDecoder));
NS_ENSURE_SUCCESS(rv, rv);
unicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
int32_t srcLen = aURI.Length();
int32_t dstLen;

View File

@ -24,6 +24,31 @@ namespace JS {
D(DEBUG_MODE_GC) \
D(TRANSPLANT) \
D(RESET) \
D(OUT_OF_NURSERY) \
D(EVICT_NURSERY) \
D(FULL_STORE_BUFFER) \
\
/* These are reserved for future use. */ \
D(RESERVED0) \
D(RESERVED1) \
D(RESERVED2) \
D(RESERVED3) \
D(RESERVED4) \
D(RESERVED5) \
D(RESERVED6) \
D(RESERVED7) \
D(RESERVED8) \
D(RESERVED9) \
D(RESERVED10) \
D(RESERVED11) \
D(RESERVED12) \
D(RESERVED13) \
D(RESERVED14) \
D(RESERVED15) \
D(RESERVED16) \
D(RESERVED17) \
D(RESERVED18) \
D(RESERVED19) \
\
/* Reasons from Firefox */ \
D(DOM_WINDOW_UTILS) \

View File

@ -580,6 +580,9 @@ class HashMapEntry
HashMapEntry(MoveRef<HashMapEntry> rhs)
: key(Move(rhs->key)), value(Move(rhs->value)) { }
typedef Key KeyType;
typedef Value ValueType;
const Key key;
Value value;
};

View File

@ -135,6 +135,7 @@ CPPSRCS = \
Memory.cpp \
Statistics.cpp \
StoreBuffer.cpp \
Nursery.cpp \
Iteration.cpp \
Zone.cpp \
Verifier.cpp \

View File

@ -334,6 +334,8 @@ private:
GROUP5_OP_JMPN = 4,
GROUP5_OP_PUSH = 6,
FPU6_OP_FLD = 0,
FPU6_OP_FISTTP = 1,
FPU6_OP_FSTP = 3,
GROUP11_MOV = 0
@ -658,8 +660,19 @@ public:
}
#endif
void fld_m(int offset, RegisterID base)
{
spew("fld %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FLD, base, offset);
}
void fisttp_m(int offset, RegisterID base)
{
spew("fisttp %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FISTTP, base, offset);
}
void fstp_m(int offset, RegisterID base)
{
spew("fstp %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FSTP, base, offset);
}

View File

@ -2493,7 +2493,11 @@ Parser<ParseHandler>::bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data
* declarations, so make sure to indicate the need to deoptimize
* the script's arguments object.
*/
if (name == cx->names().arguments) {
HandlePropertyName arguments = cx->names().arguments;
if (name == arguments) {
Node pn = parser->handler.newName(arguments, pc);
if (!pc->define(parser->context, arguments, pn, Definition::VAR))
return false;
funbox->setArgumentsHasLocalBinding();
funbox->setDefinitelyNeedsArgsObj();
}

View File

@ -10,6 +10,7 @@
#include "jsstr.h"
#include "gc/Marking.h"
#include "gc/Nursery-inl.h"
#include "methodjit/MethodJIT.h"
#include "vm/Shape.h"
@ -105,10 +106,22 @@ IsThingPoisoned(T *thing)
}
#endif
static GCMarker *
AsGCMarker(JSTracer *trc)
{
JS_ASSERT(IS_GC_MARKING_TRACER(trc));
return static_cast<GCMarker *>(trc);
}
template<typename T>
static inline void
CheckMarkedThing(JSTracer *trc, T *thing)
{
#ifdef DEBUG
/* This function uses data that's not available in the nursery. */
if (IsInsideNursery(trc->runtime, thing))
return;
JS_ASSERT(trc);
JS_ASSERT(thing);
JS_ASSERT(thing->zone());
@ -120,21 +133,20 @@ CheckMarkedThing(JSTracer *trc, T *thing)
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gcManipulatingDeadZones,
!thing->zone()->scheduledForDestruction);
#ifdef DEBUG
rt->assertValidThread();
#endif
JS_ASSERT_IF(thing->zone()->requireGCTracer(), IS_GC_MARKING_TRACER(trc));
JS_ASSERT_IF(thing->zone()->requireGCTracer(),
IS_GC_MARKING_TRACER(trc));
JS_ASSERT(thing->isAligned());
JS_ASSERT_IF(thing->isTenured(), MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
JS_ASSERT(MapTypeToTraceKind<T>::kind == GetGCThingTraceKind(thing));
JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
thing->zone()->isCollecting() ||
thing->zone() == rt->atomsCompartment->zone());
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && ((GCMarker *)trc)->getMarkColor() == GRAY,
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && AsGCMarker(trc)->getMarkColor() == GRAY,
thing->zone()->isGCMarkingGray() ||
thing->zone() == rt->atomsCompartment->zone());
@ -146,13 +158,7 @@ CheckMarkedThing(JSTracer *trc, T *thing)
*/
JS_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy(),
!InFreeList(thing->arenaHeader(), thing));
}
static GCMarker *
AsGCMarker(JSTracer *trc)
{
JS_ASSERT(IS_GC_MARKING_TRACER(trc));
return static_cast<GCMarker *>(trc);
#endif
}
template<typename T>
@ -164,15 +170,25 @@ MarkInternal(JSTracer *trc, T **thingp)
CheckMarkedThing(trc, thing);
/*
* Don't mark things outside a compartment if we are in a per-compartment
* GC.
*/
if (!trc->callback) {
if (thing->zone()->isGCMarking()) {
/*
* We may mark a Nursery thing outside the context of the
* MinorCollectionTracer because of a pre-barrier. The pre-barrier is
* not needed in this case because we perform a minor collection before
* each incremental slice.
*/
if (IsInsideNursery(trc->runtime, thing))
return;
/*
* Don't mark things outside a compartment if we are in a
* per-compartment GC.
*/
if (!thing->zone()->isGCMarking())
return;
PushMarkStack(AsGCMarker(trc), thing);
thing->zone()->maybeAlive = true;
}
} else {
trc->callback(trc, (void **)thingp, MapTypeToTraceKind<T>::kind);
JS_UNSET_TRACING_LOCATION(trc);
@ -252,6 +268,11 @@ IsMarked(T **thingp)
{
JS_ASSERT(thingp);
JS_ASSERT(*thingp);
#ifdef JSGC_GENERATIONAL
Nursery &nursery = (*thingp)->runtime()->gcNursery;
if (nursery.isInside(*thingp))
return nursery.getForwardedPointer(thingp);
#endif
Zone *zone = (*thingp)->tenuredZone();
if (!zone->isCollecting() || zone->isGCFinished())
return true;
@ -264,6 +285,11 @@ IsAboutToBeFinalized(T **thingp)
{
JS_ASSERT(thingp);
JS_ASSERT(*thingp);
#ifdef JSGC_GENERATIONAL
Nursery &nursery = (*thingp)->runtime()->gcNursery;
if (nursery.isInside(*thingp))
return !nursery.getForwardedPointer(thingp);
#endif
if (!(*thingp)->tenuredZone()->isGCSweeping())
return false;
return !(*thingp)->isMarked();
@ -328,12 +354,12 @@ DeclMarkerImpl(BaseShape, BaseShape)
DeclMarkerImpl(BaseShape, UnownedBaseShape)
DeclMarkerImpl(IonCode, ion::IonCode)
DeclMarkerImpl(Object, ArgumentsObject)
DeclMarkerImpl(Object, ArrayBufferObject)
DeclMarkerImpl(Object, DebugScopeObject)
DeclMarkerImpl(Object, GlobalObject)
DeclMarkerImpl(Object, JSObject)
DeclMarkerImpl(Object, JSFunction)
DeclMarkerImpl(Object, ScopeObject)
DeclMarkerImpl(Object, ArrayBufferObject)
DeclMarkerImpl(Script, JSScript)
DeclMarkerImpl(Shape, Shape)
DeclMarkerImpl(String, JSAtom)
@ -353,7 +379,8 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
{
JS_ASSERT(thingp);
JS_ASSERT(*thingp);
JS_ASSERT(kind == GetGCThingTraceKind(*thingp));
DebugOnly<Cell *> cell = static_cast<Cell *>(*thingp);
JS_ASSERT_IF(cell->isTenured(), kind == MapAllocToTraceKind(cell->tenuredGetAllocKind()));
switch (kind) {
case JSTRACE_OBJECT:
MarkInternal(trc, reinterpret_cast<RawObject *>(thingp));
@ -725,6 +752,7 @@ static void
PushMarkStack(GCMarker *gcmarker, JSObject *thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
gcmarker->pushObject(thing);
@ -734,6 +762,7 @@ static void
PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
gcmarker->pushObject(thing);
@ -743,6 +772,7 @@ static void
PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
gcmarker->pushType(thing);
@ -752,6 +782,7 @@ static void
PushMarkStack(GCMarker *gcmarker, RawScript thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
/*
* We mark scripts directly rather than pushing on the stack as they can
@ -769,6 +800,7 @@ static void
PushMarkStack(GCMarker *gcmarker, RawShape thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
/* We mark shapes directly rather than pushing on the stack. */
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
@ -779,6 +811,7 @@ static void
PushMarkStack(GCMarker *gcmarker, ion::IonCode *thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
gcmarker->pushIonCode(thing);
@ -791,6 +824,7 @@ static void
PushMarkStack(GCMarker *gcmarker, RawBaseShape thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
/* We mark base shapes directly rather than pushing on the stack. */
if (thing->markIfUnmarked(gcmarker->getMarkColor()))

View File

@ -12,6 +12,7 @@
#include "jslock.h"
#include "gc/Barrier.h"
#include "gc/Nursery.h"
#include "js/TemplateLib.h"
#include "ion/IonCode.h"
@ -28,6 +29,7 @@ class JSLinearString;
namespace js {
class ArgumentsObject;
class ArrayBufferObject;
class BaseShape;
class GlobalObject;
class UnownedBaseShape;
@ -92,12 +94,12 @@ DeclMarker(BaseShape, BaseShape)
DeclMarker(BaseShape, UnownedBaseShape)
DeclMarker(IonCode, ion::IonCode)
DeclMarker(Object, ArgumentsObject)
DeclMarker(Object, ArrayBufferObject)
DeclMarker(Object, DebugScopeObject)
DeclMarker(Object, GlobalObject)
DeclMarker(Object, JSObject)
DeclMarker(Object, JSFunction)
DeclMarker(Object, ScopeObject)
DeclMarker(Object, ArrayBufferObject)
DeclMarker(Script, JSScript)
DeclMarker(Shape, Shape)
DeclMarker(String, JSAtom)
@ -279,12 +281,20 @@ Mark(JSTracer *trc, HeapPtr<ion::IonCode> *code, const char *name)
MarkIonCode(trc, code, name);
}
/* For use by WeakMap's HashKeyRef instantiation. */
inline void
Mark(JSTracer *trc, JSObject **objp, const char *name)
{
MarkObjectUnbarriered(trc, objp, name);
}
/* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */
inline void
Mark(JSTracer *trc, ScopeObject **obj, const char *name)
{
MarkObjectUnbarriered(trc, obj, name);
}
bool
IsCellMarked(Cell **thingp);

83
js/src/gc/Nursery-inl.h Normal file
View File

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=79 ft=cpp:
*
* 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/. */
#ifdef JSGC_GENERATIONAL
#ifndef gc_Nursery_inl_h__
#define gc_Nursery_inl_h__
#include "gc/Heap.h"
#include "gc/Nursery.h"
namespace js {
namespace gc {
/*
* This structure overlays a Cell in the Nursery and re-purposes its memory
* for managing the Nursery collection process.
*/
class RelocationOverlay
{
friend struct MinorCollectionTracer;
/* The low bit is set so this should never equal a normal pointer. */
const static uintptr_t Relocated = uintptr_t(0xbad0bad1);
/* Set to Relocated when moved. */
uintptr_t magic_;
/* The location |this| was moved to. */
Cell *newLocation_;
/* A list entry to track all relocated things. */
RelocationOverlay *next_;
public:
static RelocationOverlay *fromCell(Cell *cell) {
JS_ASSERT(!cell->isTenured());
return reinterpret_cast<RelocationOverlay *>(cell);
}
bool isForwarded() const {
return magic_ == Relocated;
}
Cell *forwardingAddress() const {
JS_ASSERT(isForwarded());
return newLocation_;
}
void forwardTo(Cell *cell) {
JS_ASSERT(!isForwarded());
magic_ = Relocated;
newLocation_ = cell;
next_ = NULL;
}
RelocationOverlay *next() const {
return next_;
}
};
} /* namespace gc */
} /* namespace js */
template <typename T>
JS_ALWAYS_INLINE bool
js::Nursery::getForwardedPointer(T **ref)
{
JS_ASSERT(ref);
JS_ASSERT(isInside(*ref));
const gc::RelocationOverlay *overlay = reinterpret_cast<const gc::RelocationOverlay *>(*ref);
if (!overlay->isForwarded())
return false;
/* This static cast from Cell* restricts T to valid (GC thing) types. */
*ref = static_cast<T *>(overlay->forwardingAddress());
return true;
}
#endif /* gc_Nursery_inl_h__ */
#endif /* JSGC_GENERATIONAL */

516
js/src/gc/Nursery.cpp Normal file
View File

@ -0,0 +1,516 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* 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/. */
#ifdef JSGC_GENERATIONAL
#include "jscompartment.h"
#include "jsgc.h"
#include "gc/GCInternals.h"
#include "gc/Memory.h"
#include "vm/Debugger.h"
#include "gc/Barrier-inl.h"
#include "gc/Nursery-inl.h"
using namespace js;
using namespace gc;
using namespace mozilla;
bool
js::Nursery::enable()
{
if (isEnabled())
return true;
if (!hugeSlots.init())
return false;
fallbackBitmap.clear(false);
void *heap = MapAlignedPages(NurserySize, Alignment);
if (!heap)
return false;
JSRuntime *rt = runtime();
rt->gcNurseryStart_ = position_ = uintptr_t(heap);
rt->gcNurseryEnd_ = start() + NurseryUsableSize;
asLayout().runtime = rt;
JS_POISON(asLayout().data, FreshNursery, sizeof(asLayout().data));
JS_ASSERT(isEnabled());
return true;
}
void
js::Nursery::disable()
{
if (!isEnabled())
return;
hugeSlots.finish();
JS_ASSERT(start());
UnmapPages((void *)start(), NurserySize);
runtime()->gcNurseryStart_ = runtime()->gcNurseryEnd_ = position_ = 0;
}
js::Nursery::~Nursery()
{
disable();
}
void *
js::Nursery::allocate(size_t size)
{
JS_ASSERT(size % ThingAlignment == 0);
JS_ASSERT(position() % ThingAlignment == 0);
if (position() + size > end())
return NULL;
void *thing = (void *)position();
position_ = position() + size;
JS_POISON(thing, AllocatedThing, size);
return thing;
}
/* Internally, this function is used to allocate elements as well as slots. */
HeapSlot *
js::Nursery::allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots)
{
JS_ASSERT(obj);
JS_ASSERT(nslots > 0);
if (!isInside(obj))
return cx->pod_malloc<HeapSlot>(nslots);
if (nslots > MaxNurserySlots)
return allocateHugeSlots(cx, nslots);
size_t size = sizeof(HeapSlot) * nslots;
HeapSlot *slots = static_cast<HeapSlot *>(allocate(size));
if (slots)
return slots;
return allocateHugeSlots(cx, nslots);
}
ObjectElements *
js::Nursery::allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems)
{
return reinterpret_cast<ObjectElements *>(allocateSlots(cx, obj, nelems));
}
HeapSlot *
js::Nursery::reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots,
uint32_t oldCount, uint32_t newCount)
{
size_t oldSize = oldCount * sizeof(HeapSlot);
size_t newSize = newCount * sizeof(HeapSlot);
if (!isInside(obj))
return static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
if (!isInside(oldSlots)) {
HeapSlot *newSlots = static_cast<HeapSlot *>(cx->realloc_(oldSlots, oldSize, newSize));
if (oldSlots != newSlots) {
hugeSlots.remove(oldSlots);
/* If this put fails, we will only leak the slots. */
(void)hugeSlots.put(newSlots);
}
return newSlots;
}
/* The nursery cannot make use of the returned slots data. */
if (newCount < oldCount)
return oldSlots;
HeapSlot *newSlots = allocateSlots(cx, obj, newCount);
PodCopy(newSlots, oldSlots, oldCount);
return newSlots;
}
ObjectElements *
js::Nursery::reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader,
uint32_t oldCount, uint32_t newCount)
{
HeapSlot *slots = reallocateSlots(cx, obj, reinterpret_cast<HeapSlot *>(oldHeader),
oldCount, newCount);
return reinterpret_cast<ObjectElements *>(slots);
}
HeapSlot *
js::Nursery::allocateHugeSlots(JSContext *cx, size_t nslots)
{
HeapSlot *slots = cx->pod_malloc<HeapSlot>(nslots);
/* If this put fails, we will only leak the slots. */
(void)hugeSlots.put(slots);
return slots;
}
void
js::Nursery::notifyInitialSlots(Cell *cell, HeapSlot *slots)
{
if (isInside(cell) && !isInside(slots)) {
/* If this put fails, we will only leak the slots. */
(void)hugeSlots.put(slots);
}
}
namespace js {
namespace gc {
class MinorCollectionTracer : public JSTracer
{
public:
Nursery *nursery;
JSRuntime *runtime;
AutoTraceSession session;
/*
* This list is threaded through the Nursery using the space from already
* moved things. The list is used to fix up the moved things and to find
* things held live by intra-Nursery pointers.
*/
RelocationOverlay *head;
RelocationOverlay **tail;
/* Save and restore all of the runtime state we use during MinorGC. */
bool priorNeedsBarrier;
/* Insert the given relocation entry into the list of things to visit. */
JS_ALWAYS_INLINE void insertIntoFixupList(RelocationOverlay *entry) {
*tail = entry;
tail = &entry->next_;
*tail = NULL;
}
MinorCollectionTracer(JSRuntime *rt, Nursery *nursery)
: JSTracer(),
nursery(nursery),
runtime(rt),
session(runtime, MinorCollecting),
head(NULL),
tail(&head),
priorNeedsBarrier(runtime->needsBarrier())
{
JS_TracerInit(this, runtime, Nursery::MinorGCCallback);
eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
runtime->gcNumber++;
runtime->setNeedsBarrier(false);
++runtime->gcDisableStrictProxyCheckingCount;
}
~MinorCollectionTracer() {
--runtime->gcDisableStrictProxyCheckingCount;
runtime->setNeedsBarrier(priorNeedsBarrier);
}
};
} /* namespace gc */
} /* namespace js */
static AllocKind
GetObjectAllocKindForCopy(JSObject *obj)
{
if (obj->isArray()) {
JS_ASSERT(obj->numFixedSlots() == 0);
size_t nelements = obj->getDenseInitializedLength();
return GetBackgroundAllocKind(GetGCArrayKind(nelements));
}
if (obj->isFunction())
return obj->toFunction()->getAllocKind();
AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots());
if (CanBeFinalizedInBackground(kind, obj->getClass()))
kind = GetBackgroundAllocKind(kind);
return kind;
}
void *
js::Nursery::allocateFromTenured(Zone *zone, AllocKind thingKind)
{
void *t = zone->allocator.arenas.allocateFromFreeList(thingKind, Arena::thingSize(thingKind));
if (!t) {
zone->allocator.arenas.checkEmptyFreeList(thingKind);
t = zone->allocator.arenas.allocateFromArena(zone, thingKind);
}
return t;
}
void *
js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
{
Zone *zone = src->zone();
AllocKind dstKind = GetObjectAllocKindForCopy(src);
JSObject *dst = static_cast<JSObject *>(allocateFromTenured(zone, dstKind));
if (!dst)
MOZ_CRASH();
moveObjectToTenured(dst, src, dstKind);
RelocationOverlay *overlay = reinterpret_cast<RelocationOverlay *>(src);
overlay->forwardTo(dst);
trc->insertIntoFixupList(overlay);
return static_cast<void *>(dst);
}
void
js::Nursery::moveObjectToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
{
size_t srcSize = Arena::thingSize(dstKind);
/*
* Arrays do not necessarily have the same AllocKind between src and dst.
* We deal with this by copying elements manually, possibly re-inlining
* them if there is adequate room inline in dst.
*/
if (src->isArray())
srcSize = sizeof(ObjectImpl);
js_memcpy(dst, src, srcSize);
moveSlotsToTenured(dst, src, dstKind);
moveElementsToTenured(dst, src, dstKind);
/* The shape's list head may point into the old object. */
if (&src->shape_ == dst->shape_->listp)
dst->shape_->listp = &dst->shape_;
}
void
js::Nursery::moveSlotsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
{
/* Fixed slots have already been copied over. */
if (!src->hasDynamicSlots())
return;
if (!isInside(src->slots)) {
hugeSlots.remove(src->slots);
return;
}
Allocator *alloc = &src->zone()->allocator;
size_t count = src->numDynamicSlots();
dst->slots = alloc->pod_malloc<HeapSlot>(count);
PodCopy(dst->slots, src->slots, count);
}
void
js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
{
if (src->hasEmptyElements())
return;
Allocator *alloc = &src->zone()->allocator;
ObjectElements *srcHeader = src->getElementsHeader();
ObjectElements *dstHeader;
if (!isInside(srcHeader)) {
JS_ASSERT(src->elements == dst->elements);
hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
return;
}
/* ArrayBuffer stores byte-length, not Value count. */
if (src->isArrayBuffer()) {
size_t nbytes = sizeof(ObjectElements) + srcHeader->initializedLength;
if (src->hasDynamicElements()) {
dstHeader = static_cast<ObjectElements *>(alloc->malloc_(nbytes));
if (!dstHeader)
MOZ_CRASH();
} else {
dst->setFixedElements();
dstHeader = dst->getElementsHeader();
}
js_memcpy(dstHeader, srcHeader, nbytes);
dst->elements = dstHeader->elements();
return;
}
size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->initializedLength;
/* Unlike other objects, Arrays can have fixed elements. */
if (src->isArray() && nslots <= GetGCKindSlots(dstKind)) {
dst->setFixedElements();
dstHeader = dst->getElementsHeader();
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
dstHeader->capacity = GetGCKindSlots(dstKind) - ObjectElements::VALUES_PER_HEADER;
return;
}
size_t nbytes = nslots * sizeof(HeapValue);
dstHeader = static_cast<ObjectElements *>(alloc->malloc_(nbytes));
if (!dstHeader)
MOZ_CRASH();
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
dstHeader->capacity = srcHeader->initializedLength;
dst->elements = dstHeader->elements();
}
static bool
ShouldMoveToTenured(MinorCollectionTracer *trc, void **thingp)
{
Cell *cell = static_cast<Cell *>(*thingp);
Nursery &nursery = *trc->nursery;
return !nursery.isInside(thingp) && nursery.isInside(cell) &&
!nursery.getForwardedPointer(thingp);
}
/* static */ void
js::Nursery::MinorGCCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
{
MinorCollectionTracer *trc = static_cast<MinorCollectionTracer *>(jstrc);
if (ShouldMoveToTenured(trc, thingp))
*thingp = trc->nursery->moveToTenured(trc, static_cast<JSObject *>(*thingp));
}
void
js::Nursery::markFallback(Cell *cell)
{
JS_ASSERT(uintptr_t(cell) >= start());
size_t offset = uintptr_t(cell) - start();
JS_ASSERT(offset < end() - start());
JS_ASSERT(offset % ThingAlignment == 0);
fallbackBitmap.set(offset / ThingAlignment);
}
void
js::Nursery::moveFallbackToTenured(gc::MinorCollectionTracer *trc)
{
for (size_t i = 0; i < FallbackBitmapBits; ++i) {
if (fallbackBitmap.get(i)) {
JSObject *src = reinterpret_cast<JSObject *>(start() + i * ThingAlignment);
moveToTenured(trc, src);
}
}
fallbackBitmap.clear(false);
}
/* static */ void
js::Nursery::MinorFallbackMarkingCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
{
MinorCollectionTracer *trc = static_cast<MinorCollectionTracer *>(jstrc);
if (ShouldMoveToTenured(trc, thingp))
trc->nursery->markFallback(static_cast<JSObject *>(*thingp));
}
/* static */ void
js::Nursery::MinorFallbackFixupCallback(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
{
MinorCollectionTracer *trc = static_cast<MinorCollectionTracer *>(jstrc);
if (trc->nursery->isInside(*thingp))
trc->nursery->getForwardedPointer(thingp);
}
static void
TraceHeapWithCallback(JSTracer *trc, JSTraceCallback callback)
{
JSTraceCallback prior = trc->callback;
AutoCopyFreeListToArenas copy(trc->runtime);
trc->callback = callback;
for (ZonesIter zone(trc->runtime); !zone.done(); zone.next()) {
for (size_t i = 0; i < FINALIZE_LIMIT; ++i) {
AllocKind kind = AllocKind(i);
for (CellIterUnderGC cells(zone, kind); !cells.done(); cells.next())
JS_TraceChildren(trc, cells.getCell(), MapAllocToTraceKind(kind));
}
}
trc->callback = prior;
}
void
js::Nursery::markStoreBuffer(MinorCollectionTracer *trc)
{
JSRuntime *rt = trc->runtime;
if (!rt->gcStoreBuffer.hasOverflowed()) {
rt->gcStoreBuffer.mark(trc);
return;
}
/*
* If the store buffer has overflowed, we need to walk the full heap to
* discover cross-generation edges. Since we cannot easily walk the heap
* while simultaneously allocating, we use a three pass algorithm:
* 1) Walk the major heap and mark live things in the nursery in a
* pre-allocated bitmap.
* 2) Use the bitmap to move all live nursery things to the tenured
* heap.
* 3) Walk the heap a second time to find and update all of the moved
* references in the tenured heap.
*/
TraceHeapWithCallback(trc, MinorFallbackMarkingCallback);
moveFallbackToTenured(trc);
TraceHeapWithCallback(trc, MinorFallbackFixupCallback);
}
void
js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
{
JS_AbortIfWrongThread(rt);
if (!isEnabled())
return;
if (position() == start())
return;
rt->gcHelperThread.waitBackgroundSweepEnd();
/* Move objects pointed to by roots from the nursery to the major heap. */
MinorCollectionTracer trc(rt, this);
MarkRuntime(&trc);
Debugger::markAll(&trc);
for (CompartmentsIter comp(rt); !comp.done(); comp.next()) {
comp->markAllCrossCompartmentWrappers(&trc);
comp->markAllInitialShapeTableEntries(&trc);
}
markStoreBuffer(&trc);
/*
* Most of the work is done here. This loop iterates over objects that have
* been moved to the major heap. If these objects have any outgoing pointers
* to the nursery, then those nursery objects get moved as well, until no
* objects are left to move. That is, we iterate to a fixed point.
*/
for (RelocationOverlay *p = trc.head; p; p = p->next()) {
JSObject *obj = static_cast<JSObject*>(p->forwardingAddress());
JS_TraceChildren(&trc, obj, MapAllocToTraceKind(obj->tenuredGetAllocKind()));
}
/* Sweep. */
sweep(rt->defaultFreeOp());
rt->gcStoreBuffer.clear();
/*
* We ignore gcMaxBytes when allocating for minor collection. However, if we
* overflowed, we disable the nursery. The next time we allocate, we'll fail
* because gcBytes >= gcMaxBytes.
*/
if (rt->gcBytes >= rt->gcMaxBytes)
disable();
}
void
js::Nursery::sweep(FreeOp *fop)
{
for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
fop->free_(r.front());
hugeSlots.clear();
JS_POISON((void *)start(), SweptNursery, NurserySize - sizeof(JSRuntime *));
position_ = start();
}
#endif /* JSGC_GENERATIONAL */

185
js/src/gc/Nursery.h Normal file
View File

@ -0,0 +1,185 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jsgc_nursery_h___
#define jsgc_nursery_h___
#ifdef JSGC_GENERATIONAL
#include "ds/BitArray.h"
#include "js/HashTable.h"
#include "jsgc.h"
#include "jspubtd.h"
namespace js {
class ObjectElements;
namespace gc {
class MinorCollectionTracer;
} /* namespace gc */
class Nursery
{
public:
const static size_t Alignment = gc::ChunkSize;
const static size_t NurserySize = gc::ChunkSize;
const static size_t NurseryMask = NurserySize - 1;
explicit Nursery(JSRuntime *rt)
: runtime_(rt),
position_(0)
{}
~Nursery();
bool enable();
void disable();
bool isEnabled() const { return bool(start()); }
template <typename T>
JS_ALWAYS_INLINE bool isInside(const T *p) const {
return uintptr_t(p) >= start() && uintptr_t(p) < end();
}
/*
* Allocate and return a pointer to a new GC thing. Returns NULL if the
* Nursery is full.
*/
void *allocate(size_t size);
/* Allocate a slots array for the given object. */
HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots);
/* Allocate an elements vector for the given object. */
ObjectElements *allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems);
/* Resize an existing slots array. */
HeapSlot *reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots,
uint32_t oldCount, uint32_t newCount);
/* Resize an existing elements vector. */
ObjectElements *reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader,
uint32_t oldCount, uint32_t newCount);
/* Add a slots to our tracking list if it is out-of-line. */
void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots);
/* Do a minor collection. */
void collect(JSRuntime *rt, JS::gcreason::Reason reason);
/*
* Check if the thing at |*ref| in the Nursery has been forwarded. If so,
* sets |*ref| to the new location of the object and returns true. Otherwise
* returns false and leaves |*ref| unset.
*/
template <typename T>
JS_ALWAYS_INLINE bool getForwardedPointer(T **ref);
private:
/*
* The start and end pointers are stored under the runtime so that we can
* inline the isInsideNursery check into embedder code. Use the start()
* and end() functions to access these values.
*/
JSRuntime *runtime_;
/* Pointer to the first unallocated byte in the nursery. */
uintptr_t position_;
/*
* The set of externally malloced slots potentially kept live by objects
* stored in the nursery. Any external slots that do not belong to a
* tenured thing at the end of a minor GC must be freed.
*/
typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
HugeSlotsSet hugeSlots;
/* The marking bitmap for the fallback marker. */
const static size_t ThingAlignment = sizeof(Value);
const static size_t FallbackBitmapBits = NurserySize / ThingAlignment;
BitArray<FallbackBitmapBits> fallbackBitmap;
#ifdef DEBUG
/*
* In DEBUG builds, these bytes indicate the state of an unused segment of
* nursery-allocated memory.
*/
const static uint8_t FreshNursery = 0x2a;
const static uint8_t SweptNursery = 0x2b;
const static uint8_t AllocatedThing = 0x2c;
#endif
/* The maximum number of slots allowed to reside inline in the nursery. */
const static size_t MaxNurserySlots = 100;
/* The amount of space in the mapped nursery available to allocations. */
const static size_t NurseryUsableSize = NurserySize - sizeof(JSRuntime *);
struct Layout {
char data[NurseryUsableSize];
JSRuntime *runtime;
};
Layout &asLayout() {
JS_STATIC_ASSERT(sizeof(Layout) == NurserySize);
JS_ASSERT(start());
return *reinterpret_cast<Layout *>(start());
}
JS_ALWAYS_INLINE uintptr_t start() const {
JS_ASSERT(runtime_);
return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
}
JS_ALWAYS_INLINE uintptr_t end() const {
JS_ASSERT(runtime_);
return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
}
uintptr_t position() const { return position_; }
JSRuntime *runtime() const { return runtime_; }
/* Allocates and registers external slots with the nursery. */
HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots);
/* Allocates a new GC thing from the tenured generation during minor GC. */
void *allocateFromTenured(Zone *zone, gc::AllocKind thingKind);
/*
* Move the object at |src| in the Nursery to an already-allocated cell
* |dst| in Tenured.
*/
void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src);
void moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
void moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
void moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
/* Handle fallback marking. See the comment in MarkStoreBuffer. */
void markFallback(gc::Cell *cell);
void moveFallbackToTenured(gc::MinorCollectionTracer *trc);
void markStoreBuffer(gc::MinorCollectionTracer *trc);
/*
* Frees all non-live nursery-allocated things at the end of a minor
* collection. This operation takes time proportional to the number of
* dead things.
*/
void sweep(FreeOp *fop);
static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind);
static void MinorFallbackMarkingCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind);
static void MinorFallbackFixupCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind);
friend class gc::MinorCollectionTracer;
};
} /* namespace js */
#endif /* JSGC_GENERATIONAL */
#endif /* jsgc_nursery_h___ */

View File

@ -75,6 +75,13 @@ StoreBuffer::MonoTypeBuffer<T>::disable()
base = pos = top = NULL;
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::clear()
{
pos = base;
}
template <typename T>
template <typename NurseryType>
void
@ -95,7 +102,9 @@ StoreBuffer::MonoTypeBuffer<T>::compact()
#ifdef JS_GC_ZEAL
if (owner->runtime->gcVerifyPostData)
compactNotInSet(&owner->runtime->gcVerifierNursery);
else
#endif
compactNotInSet(&owner->runtime->gcNursery);
}
template <typename T>
@ -112,11 +121,30 @@ StoreBuffer::MonoTypeBuffer<T>::put(const T &v)
* care about these new entries or any overflows they cause.
*/
*pos++ = v;
if (isFull()) {
compact();
if (isFull()) {
owner->setOverflowed();
pos = base;
}
}
}
template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
{
compact();
T *cursor = base;
while (cursor != pos) {
T edge = *cursor++;
if (edge.isNullEdge())
continue;
edge.mark(trc);
}
}
template <typename T>
bool
@ -196,6 +224,27 @@ StoreBuffer::GenericBuffer::disable()
base = pos = top = NULL;
}
void
StoreBuffer::GenericBuffer::clear()
{
pos = base;
}
void
StoreBuffer::GenericBuffer::mark(JSTracer *trc)
{
uint8_t *p = base;
while (p < pos) {
unsigned size = *((unsigned *)p);
p += sizeof(unsigned);
BufferableRef *edge = reinterpret_cast<BufferableRef *>(p);
edge->mark(trc);
p += size;
}
}
bool
StoreBuffer::GenericBuffer::containsEdge(void *location) const
{
@ -212,11 +261,37 @@ StoreBuffer::GenericBuffer::containsEdge(void *location) const
return false;
}
/*** Edges ***/
void
StoreBuffer::CellPtrEdge::mark(JSTracer *trc)
{
MarkObjectRoot(trc, reinterpret_cast<JSObject**>(edge), "store buffer edge");
}
void
StoreBuffer::ValueEdge::mark(JSTracer *trc)
{
MarkValueRoot(trc, edge, "store buffer edge");
}
void
StoreBuffer::SlotEdge::mark(JSTracer *trc)
{
if (kind == HeapSlot::Element)
MarkSlot(trc, (HeapSlot*)&object->getDenseElement(offset), "store buffer edge");
else
MarkSlot(trc, &object->getSlotRef(offset), "store buffer edge");
}
/*** StoreBuffer ***/
bool
StoreBuffer::enable()
{
if (enabled)
return true;
buffer = js_malloc(TotalSize);
if (!buffer)
return false;
@ -273,6 +348,43 @@ StoreBuffer::disable()
overflowed = false;
}
bool
StoreBuffer::clear()
{
if (!enabled)
return true;
bufferVal.clear();
bufferCell.clear();
bufferSlot.clear();
bufferRelocVal.clear();
bufferRelocCell.clear();
bufferGeneric.clear();
return true;
}
void
StoreBuffer::mark(JSTracer *trc)
{
JS_ASSERT(isEnabled());
JS_ASSERT(!overflowed);
bufferVal.mark(trc);
bufferCell.mark(trc);
bufferSlot.mark(trc);
bufferRelocVal.mark(trc);
bufferRelocCell.mark(trc);
bufferGeneric.mark(trc);
}
void
StoreBuffer::setOverflowed()
{
JS_ASSERT(enabled);
overflowed = true;
}
bool
StoreBuffer::coalesceForVerification()
{

View File

@ -29,7 +29,7 @@ class VerifierNursery
HashSet<const void *, PointerHasher<const void *, 3>, SystemAllocPolicy> nursery;
public:
VerifierNursery() : nursery() {}
explicit VerifierNursery() : nursery() {}
bool enable() {
if (!nursery.initialized())
@ -53,7 +53,6 @@ class VerifierNursery
}
bool isInside(const void *cell) const {
JS_ASSERT((uintptr_t(cell) & 0x3) == 0);
return nursery.initialized() && nursery.has(cell);
}
@ -87,6 +86,7 @@ class HashKeyRef : public BufferableRef
Map *map;
Key key;
typedef typename Map::Entry::ValueType ValueType;
typedef typename Map::Ptr Ptr;
public:
@ -99,7 +99,18 @@ class HashKeyRef : public BufferableRef
return &p->key == location;
}
void mark(JSTracer *trc) {}
void mark(JSTracer *trc) {
Key prior = key;
typename Map::Ptr p = map->lookup(key);
if (!p)
return;
ValueType value = p->value;
Mark(trc, &key, "HashKeyRef");
if (prior != key) {
map->remove(prior);
map->put(key, value);
}
}
};
/*
@ -108,27 +119,6 @@ class HashKeyRef : public BufferableRef
*/
class StoreBuffer
{
#ifdef JS_GC_ZEAL
/* For verification, we approximate an infinitly large buffer. */
static const size_t ValueBufferSize = 1024 * 1024 * sizeof(Value *);
static const size_t CellBufferSize = 1024 * 1024 * sizeof(Cell **);
static const size_t SlotBufferSize = 1024 * 1024 * (sizeof(JSObject *) + 2 * sizeof(uint32_t));
static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *);
static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **);
static const size_t GenericBufferSize = 1024 * 1024 * sizeof(int);
#else
/* TODO: profile to find the ideal size for these. */
static const size_t ValueBufferSize = 1 * 1024 * sizeof(Value *);
static const size_t CellBufferSize = 2 * 1024 * sizeof(Cell **);
static const size_t SlotBufferSize = 2 * 1024 * (sizeof(JSObject *) + sizeof(uint32_t));
static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(Value *);
static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(Cell **);
static const size_t GenericBufferSize = 1 * 1024 * sizeof(int);
#endif
static const size_t TotalSize = ValueBufferSize + CellBufferSize +
SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize +
GenericBufferSize;
typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> EdgeSet;
/*
@ -155,6 +145,7 @@ class StoreBuffer
bool enable(uint8_t *region, size_t len);
void disable();
void clear();
bool isEmpty() const { return pos == base; }
bool isFull() const { JS_ASSERT(pos <= top); return pos == top; }
@ -172,6 +163,9 @@ class StoreBuffer
/* Add one item to the buffer. */
void put(const T &v);
/* Mark the source of all edges in the store buffer. */
void mark(JSTracer *trc);
/* For verification. */
bool accumulateEdges(EdgeSet &edges);
};
@ -215,6 +209,10 @@ class StoreBuffer
bool enable(uint8_t *region, size_t len);
void disable();
void clear();
/* Mark all generic edges. */
void mark(JSTracer *trc);
/* Check if a pointer is present in the buffer. */
bool containsEdge(void *location) const;
@ -263,6 +261,8 @@ class StoreBuffer
return !*edge;
}
void mark(JSTracer *trc);
CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); }
CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); }
bool isTagged() const { return bool(uintptr_t(edge) & 1); }
@ -292,6 +292,8 @@ class StoreBuffer
return !deref();
}
void mark(JSTracer *trc);
ValueEdge tagged() const { return ValueEdge((Value *)(uintptr_t(edge) | 1)); }
ValueEdge untagged() const { return ValueEdge((Value *)(uintptr_t(edge) & ~1)); }
bool isTagged() const { return bool(uintptr_t(edge) & 1); }
@ -328,6 +330,8 @@ class StoreBuffer
JS_ALWAYS_INLINE bool inRememberedSet(NurseryType *nursery) const;
JS_ALWAYS_INLINE bool isNullEdge() const;
void mark(JSTracer *trc);
};
MonoTypeBuffer<ValueEdge> bufferVal;
@ -347,11 +351,32 @@ class StoreBuffer
/* For the verifier. */
EdgeSet edgeSet;
#ifdef JS_GC_ZEAL
/* For verification, we approximate an infinitly large buffer. */
static const size_t ValueBufferSize = 1024 * 1024 * sizeof(ValueEdge);
static const size_t CellBufferSize = 1024 * 1024 * sizeof(CellPtrEdge);
static const size_t SlotBufferSize = 1024 * 1024 * sizeof(SlotEdge);
static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(ValueEdge);
static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge);
static const size_t GenericBufferSize = 1024 * 1024 * sizeof(int);
#else
/* TODO: profile to find the ideal size for these. */
static const size_t ValueBufferSize = 1 * 1024 * sizeof(ValueEdge);
static const size_t CellBufferSize = 2 * 1024 * sizeof(CellPtrEdge);
static const size_t SlotBufferSize = 2 * 1024 * sizeof(SlotEdge);
static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(ValueEdge);
static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge);
static const size_t GenericBufferSize = 1 * 1024 * sizeof(int);
#endif
static const size_t TotalSize = ValueBufferSize + CellBufferSize +
SlotBufferSize + RelocValueBufferSize + RelocCellBufferSize +
GenericBufferSize;
/* For use by our owned buffers. */
void setOverflowed() { overflowed = true; }
void setOverflowed();
public:
StoreBuffer(JSRuntime *rt)
explicit StoreBuffer(JSRuntime *rt)
: bufferVal(this), bufferCell(this), bufferSlot(this),
bufferRelocVal(this), bufferRelocCell(this), bufferGeneric(this),
runtime(rt), buffer(NULL), overflowed(false), enabled(false)
@ -361,10 +386,7 @@ class StoreBuffer
void disable();
bool isEnabled() { return enabled; }
bool clear() {
disable();
return enable();
}
bool clear();
/* Get the overflowed status. */
bool hasOverflowed() const { return overflowed; }
@ -400,6 +422,9 @@ class StoreBuffer
bufferGeneric.put(t);
}
/* Mark the source of all edges in the store buffer. */
void mark(JSTracer *trc);
/* For the verifier. */
bool coalesceForVerification();
void releaseVerificationData();

View File

@ -326,6 +326,11 @@ DisableGGCForVerification(JSRuntime *rt)
if (rt->gcVerifyPreData || rt->gcVerifyPostData)
return;
if (rt->gcNursery.isEnabled()) {
MinorGC(rt, JS::gcreason::API);
rt->gcNursery.disable();
}
if (rt->gcStoreBuffer.isEnabled())
rt->gcStoreBuffer.disable();
#endif
@ -338,8 +343,10 @@ EnableGGCAfterVerification(JSRuntime *rt)
if (rt->gcVerifyPreData || rt->gcVerifyPostData)
return;
if (rt->gcGenerationalEnabled)
if (rt->gcGenerationalEnabled) {
rt->gcNursery.enable();
rt->gcStoreBuffer.enable();
}
#endif
}
@ -764,10 +771,6 @@ js::gc::EndVerifyPostBarriers(JSRuntime *rt)
goto oom;
/* Walk the heap. */
for (CompartmentsIter comp(rt); !comp.done(); comp.next()) {
if (comp->watchpointMap)
comp->watchpointMap->markAll(trc);
}
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
for (CellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) {

View File

@ -181,7 +181,7 @@ struct Zone : private JS::shadow::Zone, public js::gc::GraphNodeBase<JS::Zone>
* tracer.
*/
bool requireGCTracer() const {
return rt->isHeapCollecting() && gcState != NoGC;
return rt->isHeapMajorCollecting() && gcState != NoGC;
}
void setGCState(CompartmentGCState state) {

View File

@ -95,18 +95,32 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
if (iter.bailoutKind() == Bailout_ArgumentCheck) {
// Temporary hack -- skip the (unused) scopeChain, because it could be
// bogus (we can fail before the scope chain slot is set). Strip the
// hasScopeChain flag and we'll check this later to run prologue().
// hasScopeChain flag. If a call object is needed, it will get handled later
// by |ThunkToInterpreter| which call |EnsureHasScopeObjects|.
iter.skip();
flags_ &= ~StackFrame::HAS_SCOPECHAIN;
// If the script binds arguments, then skip the snapshot slot reserved to hold
// its value.
if (script()->argumentsHasVarBinding())
iter.skip();
flags_ &= ~StackFrame::HAS_ARGS_OBJ;
} else {
Value v = iter.read();
if (v.isObject()) {
scopeChain_ = &v.toObject();
Value scopeChain = iter.read();
JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined());
if (scopeChain.isObject()) {
scopeChain_ = &scopeChain.toObject();
flags_ |= StackFrame::HAS_SCOPECHAIN;
if (isFunctionFrame() && fun()->isHeavyweight())
flags_ |= StackFrame::HAS_CALL_OBJ;
} else {
JS_ASSERT(v.isUndefined());
}
// The second slot will be an arguments object if the script needs one.
if (script()->argumentsHasVarBinding()) {
Value argsObj = iter.read();
JS_ASSERT(argsObj.isObject() || argsObj.isUndefined());
if (argsObj.isObject())
initArgsObj(argsObj.toObject().asArguments());
}
}
@ -125,7 +139,7 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
if (isConstructing())
JS_ASSERT(!thisv.isPrimitive());
JS_ASSERT(iter.slots() >= CountArgSlots(fun()));
JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun()));
IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u",
iter.slots(), fun()->nargs, script()->nfixed);
@ -134,7 +148,7 @@ StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
formals()[i] = arg;
}
}
exprStackSlots -= CountArgSlots(maybeFun());
exprStackSlots -= CountArgSlots(script(), maybeFun());
for (uint32_t i = 0; i < script()->nfixed; i++) {
Value slot = iter.read();
@ -498,7 +512,7 @@ ion::ReflowTypeInfo(uint32_t bailoutResult)
return true;
}
// Initialize the decl env Object and the call object of the current frame.
// Initialize the decl env Object, call object, and any arguments obj of the current frame.
bool
ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
{
@ -603,20 +617,22 @@ ion::ThunkToInterpreter(Value *vp)
fp = iter.interpFrame();
script = iter.script();
if (script->needsArgsObj()) {
// Currently IonMonkey does not compile if the script needs an
// arguments object, so the frame should not have any argument
// object yet.
JS_ASSERT(!fp->hasArgsObj());
ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, fp);
if (!argsobj) {
ArgumentsObject *argsObj;
if (fp->hasArgsObj()) {
argsObj = &fp->argsObj();
} else {
argsObj = ArgumentsObject::createExpected(cx, fp);
if (!argsObj) {
resumeMode = JSINTERP_RETHROW;
break;
}
}
// The arguments is a local binding and needsArgsObj does not
// check if it is clobbered. Ensure that the local binding
// restored during bailout before storing the arguments object
// to the slot.
SetFrameArgumentsObject(cx, fp, script, argsobj);
SetFrameArgumentsObject(cx, fp, script, argsObj);
}
++iter;
} while (fp != br->entryfp());

View File

@ -447,7 +447,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
bool invalidate, BaselineStackBuilder &builder,
MutableHandleFunction nextCallee, jsbytecode **callPC)
{
uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(fun));
uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(script, fun));
builder.resetFramePushed();
@ -508,15 +508,25 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
}
// Initialize BaselineFrame::scopeChain
// Initialize BaselineFrame's scopeChain and argsObj
JSObject *scopeChain = NULL;
ArgumentsObject *argsObj = NULL;
BailoutKind bailoutKind = iter.bailoutKind();
if (bailoutKind == Bailout_ArgumentCheck) {
// Temporary hack -- skip the (unused) scopeChain, because it could be
// bogus (we can fail before the scope chain slot is set). Strip the
// hasScopeChain flag and we'll check this later to run prologue().
// hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|,
// which calls |EnsureHasScopeObjects|.
IonSpew(IonSpew_BaselineBailouts, " Bailout_ArgumentCheck! (no valid scopeChain)");
iter.skip();
// Scripts with |argumentsHasVarBinding| have an extra slot.
if (script->argumentsHasVarBinding()) {
IonSpew(IonSpew_BaselineBailouts,
" Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
"Using empty arguments object");
iter.skip();
}
} else {
Value v = iter.read();
if (v.isObject()) {
@ -544,9 +554,19 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
scopeChain = &(script->global());
}
}
// If script maybe has an arguments object, the second slot will hold it.
if (script->argumentsHasVarBinding()) {
v = iter.read();
JS_ASSERT(v.isObject() || v.isUndefined());
if (v.isObject())
argsObj = &v.toObject().asArguments();
}
}
IonSpew(IonSpew_BaselineBailouts, " ScopeChain=%p", scopeChain);
blFrame->setScopeChain(scopeChain);
if (argsObj)
blFrame->initArgsObjUnchecked(*argsObj);
// Do not need to initialize scratchValue or returnValue fields in BaselineFrame.
// No flags are set.
@ -566,7 +586,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis();
*builder.valuePointerAtStackOffset(thisvOffset) = thisv;
JS_ASSERT(iter.slots() >= CountArgSlots(fun));
JS_ASSERT(iter.slots() >= CountArgSlots(script, fun));
IonSpew(IonSpew_BaselineBailouts, " frame slots %u, nargs %u, nfixed %u",
iter.slots(), fun->nargs, script->nfixed);
@ -1190,19 +1210,26 @@ ion::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
if (iter.isBaselineJS()) {
BaselineFrame *frame = iter.baselineFrame();
JS_ASSERT(!frame->hasArgsObj());
if (frame->script()->needsArgsObj()) {
ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame);
if (!argsobj)
// If the frame doesn't even have a scope chain set yet, then it's resuming
// into the the prologue before the scope chain is initialized. Any
// necessary args object will also be initialized there.
if (frame->scopeChain() && frame->script()->needsArgsObj()) {
ArgumentsObject *argsObj;
if (frame->hasArgsObj()) {
argsObj = &frame->argsObj();
} else {
argsObj = ArgumentsObject::createExpected(cx, frame);
if (!argsObj)
return false;
}
// The arguments is a local binding and needsArgsObj does not
// check if it is clobbered. Ensure that the local binding
// restored during bailout before storing the arguments object
// to the slot.
RootedScript script(cx, frame->script());
SetFrameArgumentsObject(cx, frame, script, argsobj);
SetFrameArgumentsObject(cx, frame, script, argsObj);
}
if (frameno == 0)

View File

@ -411,7 +411,7 @@ BaselineCompiler::emitUseCountIncrement()
// Emit no use count increments or bailouts if Ion is not
// enabled, or if the script will never be Ion-compileable
if (!ionCompileable_)
if (!ionCompileable_ && !ionOSRCompileable_)
return true;
Register scriptReg = R2.scratchReg();

View File

@ -251,11 +251,14 @@ class BaselineFrame
bool heavyweightFunPrologue(JSContext *cx);
bool initFunctionScopeObjects(JSContext *cx);
void initArgsObj(ArgumentsObject &argsobj) {
JS_ASSERT(script()->needsArgsObj());
void initArgsObjUnchecked(ArgumentsObject &argsobj) {
flags_ |= HAS_ARGS_OBJ;
argsObj_ = &argsobj;
}
void initArgsObj(ArgumentsObject &argsobj) {
JS_ASSERT(script()->needsArgsObj());
initArgsObjUnchecked(argsobj);
}
bool hasArgsObj() const {
return flags_ & HAS_ARGS_OBJ;
}

View File

@ -3346,6 +3346,18 @@ TypedArrayGetElemStubExists(ICGetElem_Fallback *stub, HandleObject obj)
return false;
}
static bool
ArgumentsGetElemStubExists(ICGetElem_Fallback *stub, ICGetElem_Arguments::Which which)
{
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (!iter->isGetElem_Arguments())
continue;
if (iter->toGetElem_Arguments()->which() == which)
return true;
}
return false;
}
static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script,
ICGetElem_Fallback *stub, HandleObject obj,
@ -3417,11 +3429,43 @@ TryAttachGetElemStub(JSContext *cx, HandleScript script, ICGetElem_Fallback *stu
return true;
}
if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() &&
!ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic))
{
IonSpew(IonSpew_BaselineIC, " Generating GetElem(MagicArgs[Int32]) stub");
ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
ICGetElem_Arguments::Magic);
ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
if (!argsStub)
return false;
stub->addNewStub(argsStub);
return true;
}
// Otherwise, GetElem is only optimized on objects.
if (!lhs.isObject())
return true;
RootedObject obj(cx, &lhs.toObject());
// Check for ArgumentsObj[int] accesses
if (obj->isArguments() && rhs.isInt32()) {
ICGetElem_Arguments::Which which = ICGetElem_Arguments::Normal;
if (obj->isStrictArguments())
which = ICGetElem_Arguments::Strict;
if (!ArgumentsGetElemStubExists(stub, which)) {
IonSpew(IonSpew_BaselineIC, " Generating GetElem(ArgsObj[Int32]) stub");
ICGetElem_Arguments::Compiler compiler(
cx, stub->fallbackMonitorStub()->firstMonitorStub(), which);
ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
if (!argsStub)
return false;
stub->addNewStub(argsStub);
return true;
}
}
if (obj->isNative()) {
// Check for NativeObject[int] dense accesses.
if (rhs.isInt32()) {
@ -3743,6 +3787,138 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
//
// GetEelem_Arguments
//
bool
ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
if (which_ == ICGetElem_Arguments::Magic) {
// Ensure that this is a magic arguments value.
masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
// Ensure that frame has not loaded different arguments object since.
masm.branchTest32(Assembler::NonZero,
Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
Imm32(BaselineFrame::HAS_ARGS_OBJ),
&failure);
// Ensure that index is an integer.
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
Register idx = masm.extractInt32(R1, ExtractTemp1);
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratch = regs.takeAny();
// Load num actual arguments
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
masm.loadPtr(actualArgs, scratch);
// Ensure idx < argc
masm.branch32(Assembler::AboveOrEqual, idx, scratch, &failure);
// Load argval
JS_ASSERT(sizeof(Value) == 8);
masm.movePtr(BaselineFrameReg, scratch);
masm.addPtr(Imm32(BaselineFrame::offsetOfArg(0)), scratch);
BaseIndex element(scratch, idx, TimesEight);
masm.loadValue(element, R0);
// Enter type monitor IC to type-check result.
EmitEnterTypeMonitorIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
JS_ASSERT(which_ == ICGetElem_Arguments::Strict ||
which_ == ICGetElem_Arguments::Normal);
bool isStrict = which_ == ICGetElem_Arguments::Strict;
Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
// Guard on input being an arguments object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register objReg = masm.extractObject(R0, ExtractTemp0);
masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure);
// Guard on index being int32
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
Register idxReg = masm.extractInt32(R1, ExtractTemp1);
// Get initial ArgsObj length value.
masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
// Test if length has been overridden.
masm.branchTest32(Assembler::NonZero,
scratchReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&failure);
// Length has not been overridden, ensure that R1 is an integer and is <= length.
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
masm.branch32(Assembler::AboveOrEqual, idxReg, scratchReg, &failure);
// Length check succeeded, now check the correct bit. We clobber potential type regs
// now. Inputs will have to be reconstructed if we fail after this point, but that's
// unlikely.
Label failureReconstructInputs;
regs = availableGeneralRegs(0);
if (regs.has(objReg))
regs.take(objReg);
if (regs.has(idxReg))
regs.take(idxReg);
if (regs.has(scratchReg))
regs.take(scratchReg);
Register argData = regs.takeAny();
Register tempReg = regs.takeAny();
// Load ArgumentsData
masm.loadPrivate(Address(objReg, ArgumentsObject::getDataSlotOffset()), argData);
// Load deletedBits bitArray pointer into scratchReg
masm.loadPtr(Address(argData, offsetof(ArgumentsData, deletedBits)), scratchReg);
// In tempReg, calculate index of word containing bit: (idx >> logBitsPerWord)
masm.movePtr(idxReg, tempReg);
masm.rshiftPtr(Imm32(JS_BITS_PER_WORD_LOG2), tempReg);
masm.loadPtr(BaseIndex(scratchReg, tempReg, ScaleFromElemWidth(sizeof(size_t))), scratchReg);
// Don't bother testing specific bit, if any bit is set in the word, fail.
masm.branchPtr(Assembler::NotEqual, scratchReg, ImmWord((size_t)0), &failureReconstructInputs);
// Load the value. use scratchReg and tempReg to form a ValueOperand to load into.
masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
regs.add(scratchReg);
regs.add(tempReg);
ValueOperand tempVal = regs.takeAnyValue();
masm.loadValue(BaseIndex(argData, idxReg, ScaleFromElemWidth(sizeof(Value))), tempVal);
// Makesure that this is not a FORWARD_TO_CALL_SLOT magic value.
masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs);
// Everything checked out, return value.
masm.moveValue(tempVal, R0);
// Type-check result
EmitEnterTypeMonitorIC(masm);
// Failed, but inputs are deconstructed into object and int, and need to be
// reconstructed into values.
masm.bind(&failureReconstructInputs);
masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// SetElem_Fallback
//
@ -4849,6 +5025,18 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
return true;
}
if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && res.isInt32()) {
IonSpew(IonSpew_BaselineIC, " Generating GetProp(MagicArgs.length) stub");
ICGetProp_ArgumentsLength::Compiler compiler(cx, ICGetProp_ArgumentsLength::Magic);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
if (!val.isObject())
return true;
@ -4878,6 +5066,22 @@ TryAttachLengthStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub
return true;
}
if (obj->isArguments() && res.isInt32()) {
IonSpew(IonSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub",
obj->isStrictArguments() ? "Strict" : "Normal");
ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Normal;
if (obj->isStrictArguments())
which = ICGetProp_ArgumentsLength::Strict;
ICGetProp_ArgumentsLength::Compiler compiler(cx, which);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
*attached = true;
stub->addNewStub(newStub);
return true;
}
return true;
}
@ -5032,9 +5236,18 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
if (op == JSOP_LENGTH && val.isMagic(JS_OPTIMIZED_ARGUMENTS)) {
// Handle arguments.length access.
if (IsOptimizedArguments(frame, val.address())) {
// TODO: attach optimized stub.
res.setInt32(frame->numActualArgs());
// Monitor result
types::TypeScript::Monitor(cx, script, pc, res);
if (!stub->addMonitorStubForValue(cx, script, res))
return false;
bool attached = false;
if (!TryAttachLengthStub(cx, script, stub, val, res, &attached))
return false;
JS_ASSERT(attached);
return true;
}
}
@ -5567,6 +5780,62 @@ ICGetProp_CallListBaseNative::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
bool
ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
if (which_ == ICGetProp_ArgumentsLength::Magic) {
// Ensure that this is lazy arguments.
masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
// Ensure that frame has not loaded different arguments object since.
masm.branchTest32(Assembler::NonZero,
Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
Imm32(BaselineFrame::HAS_ARGS_OBJ),
&failure);
Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs());
masm.loadPtr(actualArgs, R0.scratchReg());
masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
JS_ASSERT(which_ == ICGetProp_ArgumentsLength::Strict ||
which_ == ICGetProp_ArgumentsLength::Normal);
bool isStrict = which_ == ICGetProp_ArgumentsLength::Strict;
Class *clasp = isStrict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
Register scratchReg = R1.scratchReg();
// Guard on input being an arguments object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register objReg = masm.extractObject(R0, ExtractTemp0);
masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure);
// Get initial length value.
masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg);
// Test if length has been overridden.
masm.branchTest32(Assembler::NonZero,
scratchReg,
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
&failure);
// Nope, shift out arguments length and return it.
// No need to type monitor because this stub always returns Int32.
masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg);
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// SetProp_Fallback
//

View File

@ -331,6 +331,7 @@ class ICEntry
_(GetElem_String) \
_(GetElem_Dense) \
_(GetElem_TypedArray) \
_(GetElem_Arguments) \
\
_(SetElem_Fallback) \
_(SetElem_Dense) \
@ -364,6 +365,7 @@ class ICEntry
_(GetProp_CallScripted) \
_(GetProp_CallNative) \
_(GetProp_CallListBaseNative)\
_(GetProp_ArgumentsLength) \
\
_(SetProp_Fallback) \
_(SetProp_Native) \
@ -3034,6 +3036,56 @@ class ICGetElem_TypedArray : public ICStub
};
};
class ICGetElem_Arguments : public ICMonitoredStub
{
friend class ICStubSpace;
public:
enum Which { Normal, Strict, Magic };
private:
ICGetElem_Arguments(IonCode *stubCode, ICStub *firstMonitorStub, Which which)
: ICMonitoredStub(ICStub::GetElem_Arguments, stubCode, firstMonitorStub)
{
extra_ = static_cast<uint16_t>(which);
}
public:
static inline ICGetElem_Arguments *New(ICStubSpace *space, IonCode *code,
ICStub *firstMonitorStub, Which which)
{
if (!code)
return NULL;
return space->allocate<ICGetElem_Arguments>(code, firstMonitorStub, which);
}
Which which() const {
return static_cast<Which>(extra_);
}
class Compiler : public ICStubCompiler {
ICStub *firstMonitorStub_;
Which which_;
protected:
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(which_) << 16);
}
public:
Compiler(JSContext *cx, ICStub *firstMonitorStub, Which which)
: ICStubCompiler(cx, ICStub::GetElem_Arguments),
firstMonitorStub_(firstMonitorStub),
which_(which)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetElem_Arguments::New(space, getStubCode(), firstMonitorStub_, which_);
}
};
};
// SetElem
// JSOP_SETELEM
// JSOP_INITELEM
@ -4219,6 +4271,47 @@ class ICGetProp_CallListBaseNative : public ICMonitoredStub
};
};
class ICGetProp_ArgumentsLength : public ICStub
{
friend class ICStubSpace;
public:
enum Which { Normal, Strict, Magic };
protected:
ICGetProp_ArgumentsLength(IonCode *stubCode)
: ICStub(ICStub::GetProp_ArgumentsLength, stubCode)
{ }
public:
static inline ICGetProp_ArgumentsLength *New(ICStubSpace *space, IonCode *code)
{
if (!code)
return NULL;
return space->allocate<ICGetProp_ArgumentsLength>(code);
}
class Compiler : public ICStubCompiler {
protected:
Which which_;
bool generateStubCode(MacroAssembler &masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(which_) << 16);
}
public:
Compiler(JSContext *cx, Which which)
: ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength),
which_(which)
{}
ICStub *getStub(ICStubSpace *space) {
return ICGetProp_ArgumentsLength::New(space, getStubCode());
}
};
};
// SetProp
// JSOP_SETPROP
// JSOP_SETNAME

View File

@ -94,8 +94,9 @@ EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr)
void *calleeToken;
if (fp->isNonEvalFunctionFrame()) {
// CountArgSlot include |this| and the |scopeChain|.
maxArgc = CountArgSlots(fp->fun()) - 1; // -1 = discard |scopeChain|
// CountArgSlot include |this| and the |scopeChain|, and maybe |argumentsObj|
// Want to keep including this, but remove the scopeChain and any argumentsObj.
maxArgc = CountArgSlots(fp->script(), fp->fun()) - StartArgSlot(fp->script(), fp->fun());
maxArgv = fp->formals() - 1; // -1 = include |this|
// Formal arguments are the argument corresponding to the function

View File

@ -1807,21 +1807,19 @@ CodeGenerator::generateArgumentsChecks()
CompileInfo &info = gen->info();
// Indexes need to be shifted by one, to skip the scope chain slot.
JS_ASSERT(info.scopeChainSlot() == 0);
static const uint32_t START_SLOT = 1;
Label miss;
for (uint32_t i = START_SLOT; i < CountArgSlots(info.fun()); i++) {
for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
// All initial parameters are guaranteed to be MParameters.
MParameter *param = rp->getOperand(i)->toParameter();
const types::TypeSet *types = param->typeSet();
if (!types || types->unknown())
continue;
// Use ReturnReg as a scratch register here, since not all platforms
// have an actual ScratchReg.
int32_t offset = ArgToStackOffset((i - START_SLOT) * sizeof(Value));
// Calculate the offset on the stack of the argument.
// (i - info.startArgSlot()) - Compute index of arg within arg vector.
// ... * sizeof(Value) - Scale by value size.
// ArgToStackOffset(...) - Compute displacement within arg vector.
int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
Label matched;
masm.guardTypeSet(Address(StackPointer, offset), types, temp, &matched, &miss);
masm.jump(&miss);
@ -2850,6 +2848,66 @@ CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
return true;
}
typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject);
static const VMFunction NewIonArgumentsObjectInfo =
FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
bool
CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject *lir)
{
// This should be getting constructed in the first block only, and not any OSR entry blocks.
JS_ASSERT(lir->mir()->block()->id() == 0);
const LAllocation *callObj = lir->getCallObject();
Register temp = ToRegister(lir->getTemp(0));
masm.movePtr(StackPointer, temp);
masm.addPtr(Imm32(frameSize()), temp);
pushArg(ToRegister(callObj));
pushArg(temp);
return callVM(NewIonArgumentsObjectInfo, lir);
}
bool
CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir)
{
Register temp = ToRegister(lir->getTemp(0));
Register argsObj = ToRegister(lir->getArgsObject());
ValueOperand out = ToOutValue(lir);
masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
masm.loadValue(argAddr, out);
#ifdef DEBUG
Label success;
masm.branchTestMagic(Assembler::NotEqual, out, &success);
masm.breakpoint();
masm.bind(&success);
#endif
return true;
}
bool
CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir)
{
Register temp = ToRegister(lir->getTemp(0));
Register argsObj = ToRegister(lir->getArgsObject());
ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
emitPreBarrier(argAddr, MIRType_Value);
#ifdef DEBUG
Label success;
masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
masm.breakpoint();
masm.bind(&success);
#endif
masm.storeValue(value, argAddr);
return true;
}
bool
CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
{
@ -6044,6 +6102,28 @@ CodeGenerator::visitOutOfLineParallelAbort(OutOfLineParallelAbort *ool)
return true;
}
bool
CodeGenerator::visitIsCallable(LIsCallable *ins)
{
Register object = ToRegister(ins->object());
Register output = ToRegister(ins->output());
masm.loadObjClass(object, output);
// An object is callable iff (isFunction() || getClass()->call).
Label notFunction, done;
masm.branchPtr(Assembler::NotEqual, output, ImmWord(&js::FunctionClass), &notFunction);
masm.move32(Imm32(1), output);
masm.jump(&done);
masm.bind(&notFunction);
masm.cmpPtr(Address(output, offsetof(js::Class, call)), ImmWord((void *)NULL));
masm.emitSet(Assembler::NonZero, output);
masm.bind(&done);
return true;
}
bool
CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
{

View File

@ -121,6 +121,9 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitCreateThis(LCreateThis *lir);
bool visitCreateThisWithProto(LCreateThisWithProto *lir);
bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir);
bool visitCreateArgumentsObject(LCreateArgumentsObject *lir);
bool visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir);
bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir);
bool visitReturnFromCtor(LReturnFromCtor *lir);
bool visitArrayLength(LArrayLength *lir);
bool visitTypedArrayLength(LTypedArrayLength *lir);
@ -214,6 +217,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitSetDOMProperty(LSetDOMProperty *lir);
bool visitCallDOMNative(LCallDOMNative *lir);
bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir);
bool visitIsCallable(LIsCallable *lir);
bool visitAsmJSCall(LAsmJSCall *lir);
bool visitAsmJSParameter(LAsmJSParameter *lir);
bool visitAsmJSReturn(LAsmJSReturn *ret);

View File

@ -13,9 +13,17 @@ namespace js {
namespace ion {
inline unsigned
CountArgSlots(JSFunction *fun)
StartArgSlot(RawScript script, JSFunction *fun)
{
return fun ? fun->nargs + 2 : 1; // +2 for |scopeChain| and |this|, or +1 for |scopeChain|
// First slot is for scope chain.
// Second one may be for arguments object.
return 1 + (script->argumentsHasVarBinding() ? 1 : 0);
}
inline unsigned
CountArgSlots(RawScript script, JSFunction *fun)
{
return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0);
}
enum ExecutionMode {
@ -37,7 +45,8 @@ class CompileInfo
executionMode_(executionMode)
{
JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
nimplicit_ = 1 /* scope chain */ + (fun ? 1 /* this */: 0);
nimplicit_ = StartArgSlot(script, fun) /* scope chain and argument obj */
+ (fun ? 1 : 0); /* this */
nargs_ = fun ? fun->nargs : 0;
nlocals_ = script->nfixed;
nstack_ = script->nslots - script->nfixed;
@ -117,17 +126,30 @@ class CompileInfo
JS_ASSERT(script());
return 0;
}
uint32_t thisSlot() const {
JS_ASSERT(fun());
uint32_t argsObjSlot() const {
JS_ASSERT(hasArguments());
return 1;
}
uint32_t firstArgSlot() const {
uint32_t thisSlot() const {
JS_ASSERT(fun());
return hasArguments() ? 2 : 1;
}
uint32_t firstActualArgSlot() const {
return nimplicit_;
}
uint32_t argSlot(uint32_t i) const {
uint32_t argSlotUnchecked(uint32_t i) const {
// During initialization, some routines need to get at arg
// slots regardless of how regular argument access is done.
JS_ASSERT(i < nargs_);
return nimplicit_ + i;
}
uint32_t argSlot(uint32_t i) const {
// This should only be accessed when compiling functions for
// which argument accesses don't need to go through the
// argument object.
JS_ASSERT(!argsObjAliasesFormals());
return argSlotUnchecked(i);
}
uint32_t firstLocalSlot() const {
return nimplicit_ + nargs_;
}
@ -141,9 +163,28 @@ class CompileInfo
return firstStackSlot() + i;
}
bool hasArguments() {
uint32_t startArgSlot() const {
JS_ASSERT(scopeChainSlot() == 0);
return StartArgSlot(script(), fun());
}
uint32_t endArgSlot() const {
JS_ASSERT(scopeChainSlot() == 0);
return CountArgSlots(script(), fun());
}
uint32_t totalSlots() const {
return 2 + (hasArguments() ? 1 : 0) + nargs() + nlocals();
}
bool hasArguments() const {
return script()->argumentsHasVarBinding();
}
bool needsArgsObj() const {
return script()->needsArgsObj();
}
bool argsObjAliasesFormals() const {
return script()->argsObjAliasesFormals();
}
ExecutionMode executionMode() const {
return executionMode_;

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