mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 06:45:42 +00:00
Merge inbound to m-c.
This commit is contained in:
commit
1c3243798f
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
instance = gBrowser.selectedTab.__responsiveUI;
|
||||
executeSoon(testCustomPresetsNotInListAnymore);
|
||||
info("waiting for responsive mode to turn on");
|
||||
mgr.once("on", () => {
|
||||
instance = gBrowser.selectedTab.__responsiveUI;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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,16 +928,11 @@ 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);
|
||||
break;
|
||||
if (json.permit) {
|
||||
let tab = this.getTabForBrowser(browser);
|
||||
BrowserUI.animateClosingTab(tab);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Browser:ZoomToPoint:Return":
|
||||
if (json.zoomTo) {
|
||||
@ -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";
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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 });
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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>
|
425
browser/metro/base/tests/mochitest/browser_selection_basic.js
Normal file
425
browser/metro/base/tests/mochitest/browser_selection_basic.js
Normal 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();
|
||||
}
|
@ -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>
|
@ -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();
|
||||
}
|
@ -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>
|
158
browser/metro/base/tests/mochitest/browser_selection_textarea.js
Normal file
158
browser/metro/base/tests/mochitest/browser_selection_textarea.js
Normal 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();
|
||||
}
|
@ -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.
|
||||
|
@ -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>
|
||||
@ -32,7 +32,7 @@
|
||||
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
|
||||
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
|
||||
|
@ -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"
|
||||
},
|
||||
]
|
||||
|
BIN
browser/metro/theme/images/pinned-hdpi.png
Normal file
BIN
browser/metro/theme/images/pinned-hdpi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
browser/metro/theme/images/pinned.png
Normal file
BIN
browser/metro/theme/images/pinned.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 999 B |
BIN
browser/metro/theme/images/tile-selected-check-hdpi.png
Normal file
BIN
browser/metro/theme/images/tile-selected-check-hdpi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 666 B |
@ -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)
|
||||
|
@ -473,9 +473,22 @@ richgriditem .richgrid-item-content {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
richgriditem[selected] .richgrid-item-content {
|
||||
border: @metro_border_xthick@ solid @selected_color@;
|
||||
padding: @metro_spacing_xxsmall@;
|
||||
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@;
|
||||
}
|
||||
|
||||
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;
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
outline: 1px solid rgb(255,153,0);
|
||||
background-color: rgba(255,153,0,0.6);
|
||||
color: rgb(153,51,0);
|
||||
|
||||
|
||||
/* tile pinned-state indication */
|
||||
richgriditem[pinned] .richgrid-item-content::before {
|
||||
pointer-events:none;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
right: 0;
|
||||
left: auto;
|
||||
top: 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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -344,6 +344,11 @@ class nsObjectLoadingContent : public nsImageLoadingContent
|
||||
*/
|
||||
ParameterUpdateFlags UpdateObjectParameters();
|
||||
|
||||
/**
|
||||
* Queue a CheckPluginStopEvent and track it in mPendingCheckPluginStopEvent
|
||||
*/
|
||||
void QueueCheckPluginStopEvent();
|
||||
|
||||
void NotifyContentObjectWrapper();
|
||||
|
||||
/**
|
||||
|
65
content/canvas/src/CanvasPattern.h
Normal file
65
content/canvas/src/CanvasPattern.h
Normal 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
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
45
content/canvas/src/TextMetrics.h
Normal file
45
content/canvas/src/TextMetrics.h
Normal 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
|
@ -7,8 +7,10 @@
|
||||
MODULE = 'content'
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'CanvasPattern.h',
|
||||
'CanvasRenderingContext2D.h',
|
||||
'CanvasUtils.h',
|
||||
'ImageData.h',
|
||||
'TextMetrics.h',
|
||||
]
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
||||
|
@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
LIBRARY_NAME = gkconhtmlcon_s
|
||||
LIBXUL_LIBRARY = 1
|
||||
FAIL_ON_WARNINGS = 1
|
||||
|
||||
CPPSRCS = \
|
||||
HTMLPropertiesCollection.cpp \
|
||||
|
@ -255,6 +255,8 @@ public:
|
||||
return nsIDocument::GetLocation();
|
||||
}
|
||||
|
||||
virtual nsHTMLDocument* AsHTMLDocument() { return this; }
|
||||
|
||||
protected:
|
||||
nsresult GetBodySize(int32_t* aWidth,
|
||||
int32_t* aHeight);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -120,8 +120,6 @@ DOMCI_CLASS(SVGZoomEvent)
|
||||
|
||||
// Canvas
|
||||
DOMCI_CLASS(CanvasGradient)
|
||||
DOMCI_CLASS(CanvasPattern)
|
||||
DOMCI_CLASS(TextMetrics)
|
||||
DOMCI_CLASS(MozCanvasPrintState)
|
||||
|
||||
// WindowUtils
|
||||
|
@ -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
|
||||
@ -1497,18 +1503,23 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
|
||||
// clearing |aObj|'s reserved slot the reserved slot of |newobj| will be
|
||||
// set to null. |aObj| will go away soon, because we swap it with
|
||||
// another object during the transplant and let that object die.
|
||||
JSObject *propertyHolder;
|
||||
JSObject* propertyHolder;
|
||||
{
|
||||
AutoCloneDOMObjectSlotGuard cloneGuard(aObj, newobj);
|
||||
|
||||
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr,
|
||||
newParent);
|
||||
if (!propertyHolder) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
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)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else {
|
||||
propertyHolder = nullptr;
|
||||
}
|
||||
|
||||
// Expandos from other compartments are attached to the target JS object.
|
||||
@ -1557,8 +1568,18 @@ ReparentWrapper(JSContext* aCx, JS::HandleObject aObjArg)
|
||||
cache->SetPreservingWrapper(false);
|
||||
cache->SetWrapper(aObj);
|
||||
cache->SetPreservingWrapper(preserving);
|
||||
if (!JS_CopyPropertiesFrom(aCx, aObj, propertyHolder)) {
|
||||
MOZ_CRASH();
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -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,7 +996,12 @@ DOMInterfaces = {
|
||||
'implicitJSContext': [ 'encode' ],
|
||||
}],
|
||||
|
||||
'TimeRanges': {
|
||||
'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)
|
||||
|
@ -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
|
||||
|
||||
@ -3460,7 +3463,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
"}\n" +
|
||||
successCode) % (wrapCall)
|
||||
return str
|
||||
|
||||
|
||||
if type is None or type.isVoid():
|
||||
return (setValue("JSVAL_VOID"), True)
|
||||
|
||||
@ -3542,11 +3545,15 @@ 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)
|
||||
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
|
||||
if descriptor.nativeOwnership == 'owned':
|
||||
wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
|
||||
else:
|
||||
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
|
||||
wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
|
||||
if not descriptor.hasXPConnectImpls:
|
||||
# Can only fail to wrap as a new-binding object
|
||||
@ -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] }
|
||||
|
@ -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*
|
||||
|
@ -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;
|
||||
|
19
dom/bindings/crashtests/862092.html
Normal file
19
dom/bindings/crashtests/862092.html
Normal 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>
|
@ -5,3 +5,4 @@ load 832899.html
|
||||
load 860591.html
|
||||
load 860551.html
|
||||
load 862610.html
|
||||
load 862092.html
|
||||
|
@ -72,6 +72,7 @@ MOCHITEST_FILES := \
|
||||
test_queryInterface.html \
|
||||
test_exceptionThrowing.html \
|
||||
test_bug852846.html \
|
||||
test_bug862092.html \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_CHROME_FILES = \
|
||||
|
37
dom/bindings/test/test_bug862092.html
Normal file
37
dom/bindings/test/test_bug862092.html
Normal 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>
|
@ -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).
|
||||
|
@ -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 \
|
||||
|
19
dom/tests/mochitest/general/file_showModalDialog.html
Normal file
19
dom/tests/mochitest/general/file_showModalDialog.html
Normal 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>
|
33
dom/tests/mochitest/general/test_showModalDialog.html
Normal file
33
dom/tests/mochitest/general/test_showModalDialog.html
Normal 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>
|
@ -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;
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
31
dom/webidl/DragEvent.webidl
Normal file
31
dom/webidl/DragEvent.webidl
Normal 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);
|
||||
};
|
@ -58,6 +58,7 @@ webidl_files = \
|
||||
DOMStringMap.webidl \
|
||||
DOMTokenList.webidl \
|
||||
DOMTransaction.webidl \
|
||||
DragEvent.webidl \
|
||||
DummyBinding.webidl \
|
||||
DynamicsCompressorNode.webidl \
|
||||
Element.webidl \
|
||||
|
@ -1473,6 +1473,7 @@ void AsyncPanZoomController::SetState(PanZoomState aState) {
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TimeoutTouchListeners() {
|
||||
mTouchListenerTimeoutTask = nullptr;
|
||||
ContentReceivedTouch(false);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -135,6 +135,7 @@ CPPSRCS = \
|
||||
Memory.cpp \
|
||||
Statistics.cpp \
|
||||
StoreBuffer.cpp \
|
||||
Nursery.cpp \
|
||||
Iteration.cpp \
|
||||
Zone.cpp \
|
||||
Verifier.cpp \
|
||||
|
@ -334,7 +334,9 @@ private:
|
||||
GROUP5_OP_JMPN = 4,
|
||||
GROUP5_OP_PUSH = 6,
|
||||
|
||||
FPU6_OP_FSTP = 3,
|
||||
FPU6_OP_FLD = 0,
|
||||
FPU6_OP_FISTTP = 1,
|
||||
FPU6_OP_FSTP = 3,
|
||||
|
||||
GROUP11_MOV = 0
|
||||
} GroupOpcodeID;
|
||||
@ -658,9 +660,20 @@ 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)
|
||||
{
|
||||
m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FSTP, base, offset);
|
||||
spew("fstp %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
|
||||
m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FSTP, base, offset);
|
||||
}
|
||||
|
||||
void negl_r(RegisterID dst)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()) {
|
||||
PushMarkStack(AsGCMarker(trc), thing);
|
||||
thing->zone()->maybeAlive = true;
|
||||
}
|
||||
/*
|
||||
* 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()))
|
||||
|
@ -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
83
js/src/gc/Nursery-inl.h
Normal 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
516
js/src/gc/Nursery.cpp
Normal 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
185
js/src/gc/Nursery.h
Normal 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___ */
|
@ -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>
|
||||
@ -113,8 +122,27 @@ StoreBuffer::MonoTypeBuffer<T>::put(const T &v)
|
||||
*/
|
||||
*pos++ = v;
|
||||
if (isFull()) {
|
||||
owner->setOverflowed();
|
||||
pos = base;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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()) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
resumeMode = JSINTERP_RETHROW;
|
||||
break;
|
||||
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());
|
||||
|
@ -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)
|
||||
return false;
|
||||
// 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)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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), ¬Function);
|
||||
masm.move32(Imm32(1), output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(¬Function);
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user