Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2013-05-22 11:28:59 +01:00
Родитель 9d91c011b4 ac855585e9
Коммит 6a91572c71
192 изменённых файлов: 4452 добавлений и 2099 удалений

Просмотреть файл

@ -112,10 +112,12 @@ this.AccessFu = {
TouchAdapter.start();
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
Services.obs.addObserver(this, 'Accessibility:NextObject', false);
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
Services.obs.addObserver(this, 'Accessibility:Focus', false);
Utils.win.addEventListener('TabOpen', this);
Utils.win.addEventListener('TabClose', this);
Utils.win.addEventListener('TabSelect', this);
if (this.readyCallback) {
@ -147,9 +149,11 @@ this.AccessFu = {
TouchAdapter.stop();
Utils.win.removeEventListener('TabOpen', this);
Utils.win.removeEventListener('TabClose', this);
Utils.win.removeEventListener('TabSelect', this);
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
Services.obs.removeObserver(this, 'in-process-browser-or-app-frame-shown');
Services.obs.removeObserver(this, 'Accessibility:NextObject');
Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
Services.obs.removeObserver(this, 'Accessibility:Focus');
@ -280,6 +284,7 @@ this.AccessFu = {
}
break;
case 'remote-browser-frame-shown':
case 'in-process-browser-or-app-frame-shown':
{
let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager;
this._handleMessageManager(mm);
@ -310,6 +315,16 @@ this.AccessFu = {
this._handleMessageManager(mm);
break;
}
case 'TabClose':
{
let mm = Utils.getMessageManager(aEvent.target);
let mmIndex = this._processedMessageManagers.indexOf(mm);
if (mmIndex > -1) {
this._removeMessageListeners(mm);
this._processedMessageManagers.splice(mmIndex, 1);
}
break;
}
case 'TabSelect':
{
if (this._focused) {
@ -464,9 +479,20 @@ var Output = {
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top);
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0, offsetY: 0 };
let browserOffset = aBrowser.getBoundingClientRect();
let root = Utils.win;
let offset = { left: -root.mozInnerScreenX, top: -root.mozInnerScreenY };
let scale = 1 / Utils.getPixelsPerCSSPixel(Utils.win);
return bounds.translate(browserOffset.left, browserOffset.top).
if (!aBrowser.contentWindow) {
// OOP browser, add offset of browser.
// The offset of the browser element in relation to its parent window.
let clientRect = aBrowser.getBoundingClientRect();
let win = aBrowser.ownerDocument.defaultView;
offset.left += clientRect.left + win.mozInnerScreenX;
offset.top += clientRect.top + win.mozInnerScreenY;
}
return bounds.scale(scale, scale).translate(offset.left, offset.top).
scale(vp.zoom, vp.zoom).expandToIntegers();
}
};

Просмотреть файл

@ -2,33 +2,58 @@
* 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/. */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
'use strict';
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
Cu.import('resource://gre/modules/accessibility/Presentation.jsm');
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
'resource://gre/modules/accessibility/Presentation.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'TraversalRules',
'resource://gre/modules/accessibility/TraversalRules.jsm');
this.EXPORTED_SYMBOLS = ['EventManager'];
this.EventManager = {
this.EventManager = function EventManager(aContentScope) {
this.contentScope = aContentScope;
this.addEventListener = this.contentScope.addEventListener.bind(
this.contentScope);
this.removeEventListener = this.contentScope.removeEventListener.bind(
this.contentScope);
this.sendMsgFunc = this.contentScope.sendAsyncMessage.bind(
this.contentScope);
this.webProgress = this.contentScope.docShell.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebProgress);
};
this.EventManager.prototype = {
editState: {},
start: function start(aSendMsgFunc) {
start: function start() {
try {
if (!this._started) {
this.sendMsgFunc = aSendMsgFunc || function() {};
Logger.info('EventManager.start', Utils.MozBuildApp);
this._started = true;
Services.obs.addObserver(this, 'accessible-event', false);
}
AccessibilityEventObserver.addListener(this);
this.webProgress.addProgressListener(this,
(Ci.nsIWebProgress.NOTIFY_STATE_ALL |
Ci.nsIWebProgress.NOTIFY_LOCATION));
this.addEventListener('scroll', this, true);
this.addEventListener('resize', this, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
this.addEventListener('DOMActivate', this, true);
}
this.present(Presentation.tabStateChanged(null, 'newtab'));
} catch (x) {
@ -37,13 +62,25 @@ this.EventManager = {
}
},
// XXX: Stop is not called when the tab is closed (|TabClose| event is too
// late). It is only called when the AccessFu is disabled explicitly.
stop: function stop() {
if (!this._started) {
return;
}
Logger.info('EventManager.stop', Utils.MozBuildApp);
Services.obs.removeObserver(this, 'accessible-event');
AccessibilityEventObserver.removeListener(this);
try {
this.webProgress.removeProgressListener(this);
this.removeEventListener('scroll', this, true);
this.removeEventListener('resize', this, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
this.removeEventListener('DOMActivate', this, true);
} catch (x) {
// contentScope is dead.
} finally {
this._started = false;
}
},
handleEvent: function handleEvent(aEvent) {
@ -85,21 +122,6 @@ this.EventManager = {
}
},
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case 'accessible-event':
var event;
try {
event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
this.handleAccEvent(event);
} catch (x) {
Logger.error('Error handing accessible event');
Logger.logException(x);
return;
}
}
},
handleAccEvent: function handleAccEvent(aEvent) {
if (Logger.logLevel >= Logger.DEBUG)
Logger.debug('A11yEvent', Logger.eventToString(aEvent),
@ -116,7 +138,7 @@ this.EventManager = {
case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
{
let pivot = aEvent.accessible.
QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
let position = pivot.position;
if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME)
break;
@ -227,11 +249,6 @@ this.EventManager = {
this.sendMsgFunc("AccessFu:Present", aPresentationData);
},
presentVirtualCursorPosition: function presentVirtualCursorPosition(aVirtualCursor) {
this.present(Presentation.pivotChanged(aVirtualCursor.position, null,
Ci.nsIAccessiblePivot.REASON_NONE));
},
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
let tabstate = '';
@ -269,3 +286,145 @@ this.EventManager = {
Ci.nsISupports,
Ci.nsIObserver])
};
const AccessibilityEventObserver = {
/**
* A WeakMap containing [content, EventManager] pairs.
*/
eventManagers: new WeakMap(),
/**
* A total number of registered eventManagers.
*/
listenerCount: 0,
/**
* An indicator of an active 'accessible-event' observer.
*/
started: false,
/**
* Start an AccessibilityEventObserver.
*/
start: function start() {
if (this.started || this.listenerCount === 0) {
return;
}
Services.obs.addObserver(this, 'accessible-event', false);
this.started = true;
},
/**
* Stop an AccessibilityEventObserver.
*/
stop: function stop() {
if (!this.started) {
return;
}
Services.obs.removeObserver(this, 'accessible-event');
// Clean up all registered event managers.
this.eventManagers.clear();
this.listenerCount = 0;
this.started = false;
},
/**
* Register an EventManager and start listening to the
* 'accessible-event' messages.
*
* @param aEventManager EventManager
* An EventManager object that was loaded into the specific content.
*/
addListener: function addListener(aEventManager) {
let content = aEventManager.contentScope.content;
if (!this.eventManagers.has(content)) {
this.listenerCount++;
}
this.eventManagers.set(content, aEventManager);
// Since at least one EventManager was registered, start listening.
Logger.debug('AccessibilityEventObserver.addListener. Total:',
this.listenerCount);
this.start();
},
/**
* Unregister an EventManager and, optionally, stop listening to the
* 'accessible-event' messages.
*
* @param aEventManager EventManager
* An EventManager object that was stopped in the specific content.
*/
removeListener: function removeListener(aEventManager) {
let content = aEventManager.contentScope.content;
if (!this.eventManagers.delete(content)) {
return;
}
this.listenerCount--;
Logger.debug('AccessibilityEventObserver.removeListener. Total:',
this.listenerCount);
if (this.listenerCount === 0) {
// If there are no EventManagers registered at the moment, stop listening
// to the 'accessible-event' messages.
this.stop();
}
},
/**
* Lookup an EventManager for a specific content. If the EventManager is not
* found, walk up the hierarchy of parent windows.
* @param content Window
* A content Window used to lookup the corresponding EventManager.
*/
getListener: function getListener(content) {
let eventManager = this.eventManagers.get(content);
if (eventManager) {
return eventManager;
}
let parent = content.parent;
if (parent === content) {
// There is no parent or the parent is of a different type.
return null;
}
return this.getListener(parent);
},
/**
* Handle the 'accessible-event' message.
*/
observe: function observe(aSubject, aTopic, aData) {
if (aTopic !== 'accessible-event') {
return;
}
let event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
if (!event.accessibleDocument) {
Logger.warning(
'AccessibilityEventObserver.observe: no accessible document:',
Logger.eventToString(event), "accessible:",
Logger.accessibleToString(event.accessible));
return;
}
let content = event.accessibleDocument.window;
// Match the content window to its EventManager.
let eventManager = this.getListener(content);
if (!eventManager || !eventManager._started) {
if (Utils.MozBuildApp === 'browser' &&
!(content instanceof Ci.nsIDOMChromeWindow)) {
Logger.warning(
'AccessibilityEventObserver.observe: ignored event:',
Logger.eventToString(event), "accessible:",
Logger.accessibleToString(event.accessible), "document:",
Logger.accessibleToString(event.accessibleDocument));
}
return;
}
try {
eventManager.handleAccEvent(event);
} catch (x) {
Logger.error('Error handing accessible event');
Logger.logException(x);
} finally {
return;
}
}
};

Просмотреть файл

@ -31,9 +31,9 @@ BaseTraversalRule.prototype = {
match: function BaseTraversalRule_match(aAccessible)
{
if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
return (aAccessible.childCount) ?
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE :
Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
return (Utils.getMessageManager(aAccessible.DOMNode)) ?
Ci.nsIAccessibleTraversalRule.FILTER_MATCH :
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
}
if (this._matchFunc)

Просмотреть файл

@ -37,6 +37,9 @@ this.Utils = {
},
get win() {
if (!this._win) {
return null;
}
return this._win.get();
},
@ -90,6 +93,9 @@ this.Utils = {
},
get BrowserApp() {
if (!this.win) {
return null;
}
switch (this.MozBuildApp) {
case 'mobile/android':
return this.win.BrowserApp;
@ -103,6 +109,9 @@ this.Utils = {
},
get CurrentBrowser() {
if (!this.BrowserApp) {
return null;
}
if (this.MozBuildApp == 'b2g')
return this.BrowserApp.contentBrowser;
return this.BrowserApp.selectedBrowser;
@ -122,15 +131,27 @@ this.Utils = {
let document = this.CurrentContentDoc;
if (document) {
let remoteframes = document.querySelectorAll('iframe[remote=true]');
let remoteframes = document.querySelectorAll('iframe');
for (let i = 0; i < remoteframes.length; ++i) {
let mm = this.getMessageManager(remoteframes[i]);
if (mm) {
messageManagers.push(mm);
}
}
for (let i = 0; i < remoteframes.length; ++i)
messageManagers.push(this.getMessageManager(remoteframes[i]));
}
return messageManagers;
},
get isContentProcess() {
delete this.isContentProcess;
this.isContentProcess =
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
return this.isContentProcess;
},
getMessageManager: function getMessageManager(aBrowser) {
try {
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
@ -164,15 +185,12 @@ this.Utils = {
let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
this.AccRetrieval.getAccessibleFor(aDocument);
while (doc) {
try {
return doc.QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
} catch (x) {
doc = doc.parentDocument;
}
}
return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
},
return null;
getPixelsPerCSSPixel: function getPixelsPerCSSPixel(aWindow) {
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
}
};
@ -228,9 +246,10 @@ this.Logger = {
logException: function logException(aException) {
try {
this.error(
aException.message,
'(' + aException.fileName + ':' + aException.lineNumber + ')');
let args = [aException.message];
args.push.apply(args, aException.stack ? ['\n', aException.stack] :
['(' + aException.fileName + ':' + aException.lineNumber + ')']);
this.error.apply(this, args);
} catch (x) {
this.error(x);
}
@ -392,15 +411,7 @@ PivotContext.prototype = {
this._accessible.getBounds(objX, objY, objW, objH);
// XXX: OOP content provides a screen offset of 0, while in-process provides a real
// offset. Removing the offset and using content-relative coords normalizes this.
let docX = {}, docY = {};
let docRoot = this._accessible.rootDocument.
QueryInterface(Ci.nsIAccessible);
docRoot.getBounds(docX, docY, {}, {});
this._bounds = new Rect(objX.value, objY.value, objW.value, objH.value).
translate(-docX.value, -docY.value);
this._bounds = new Rect(objX.value, objY.value, objW.value, objH.value);
}
return this._bounds.clone();

Просмотреть файл

@ -2,18 +2,25 @@
* 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/. */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
Cu.import('resource://gre/modules/accessibility/EventManager.jsm');
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
'resource://gre/modules/accessibility/Presentation.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'TraversalRules',
'resource://gre/modules/accessibility/TraversalRules.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'EventManager',
'resource://gre/modules/accessibility/EventManager.jsm');
Logger.debug('content-script.js');
let eventManager = null;
function virtualCursorControl(aMessage) {
if (Logger.logLevel >= Logger.DEBUG)
Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
@ -52,14 +59,21 @@ function virtualCursorControl(aMessage) {
}
break;
case 'moveToPoint':
moved = vc.moveToPoint(rule, details.x, details.y, true);
if (!this._ppcp) {
this._ppcp = Utils.getPixelsPerCSSPixel(content);
}
moved = vc.moveToPoint(rule,
details.x * this._ppcp, details.y * this._ppcp,
true);
break;
case 'whereIsIt':
if (!forwardMessage(vc, aMessage)) {
if (!vc.position && aMessage.json.move)
vc.moveFirst(TraversalRules.Simple);
else
EventManager.presentVirtualCursorPosition(vc);
else {
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
}
}
break;
@ -88,10 +102,12 @@ function forwardMessage(aVirtualCursor, aMessage) {
let mm = Utils.getMessageManager(acc.DOMNode);
mm.addMessageListener(aMessage.name, virtualCursorControl);
aMessage.json.origin = 'parent';
if (Utils.isContentProcess) {
// XXX: OOP content's screen offset is 0,
// so we remove the real screen offset here.
aMessage.json.x -= content.mozInnerScreenX;
aMessage.json.y -= content.mozInnerScreenY;
}
mm.sendAsyncMessage(aMessage.name, aMessage.json);
return true;
}
@ -124,14 +140,14 @@ function activateCurrent(aMessage) {
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
function dispatchMouseEvent(aEventType) {
let evt = content.document.createEvent("MouseEvents");
let evt = content.document.createEvent('MouseEvents');
evt.initMouseEvent(aEventType, true, true, content,
x, y, 0, 0, 0, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
dispatchMouseEvent("mousedown");
dispatchMouseEvent("mouseup");
dispatchMouseEvent('mousedown');
dispatchMouseEvent('mouseup');
}
}
@ -234,20 +250,10 @@ addMessageListener(
addMessageListener('AccessFu:Activate', activateCurrent);
addMessageListener('AccessFu:Scroll', scroll);
EventManager.start(
function sendMessage(aName, aDetails) {
sendAsyncMessage(aName, aDetails);
});
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebProgress).
addProgressListener(EventManager,
(Ci.nsIWebProgress.NOTIFY_STATE_ALL |
Ci.nsIWebProgress.NOTIFY_LOCATION));
addEventListener('scroll', EventManager, true);
addEventListener('resize', EventManager, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
addEventListener('DOMActivate', EventManager, true);
if (!eventManager) {
eventManager = new EventManager(this);
}
eventManager.start();
});
addMessageListener(
@ -259,15 +265,7 @@ addMessageListener(
removeMessageListener('AccessFu:Activate', activateCurrent);
removeMessageListener('AccessFu:Scroll', scroll);
EventManager.stop();
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebProgress).
removeProgressListener(EventManager);
removeEventListener('scroll', EventManager, true);
removeEventListener('resize', EventManager, true);
// XXX: Ideally this would be an a11y event. Bug #742280.
removeEventListener('DOMActivate', EventManager, true);
eventManager.stop();
});
sendAsyncMessage('AccessFu:Ready');

Просмотреть файл

@ -38,21 +38,23 @@
// Firs listen for initial 'EventManager.start' and disable AccessFu.
var initialStartListener = makeEventManagerListener("EventManager.start",
function () {
ok(EventManager._started, "EventManager was started.");
ok(true, "EventManager was started.");
Services.console.registerListener(stopListener);
AccessFu._disable();
});
// Listen for 'EventManager.stop' and enable AccessFu again.
var stopListener = makeEventManagerListener("EventManager.stop",
function () {
isnot(EventManager._started, true, "EventManager was stopped.");
ok(true, "EventManager was stopped.");
isnot(AccessFu._enabled, true, "AccessFu was disabled.");
Services.console.registerListener(finalStartListener);
AccessFu._enable();
});
// Make sure EventManager is started again.
var finalStartListener = makeEventManagerListener("EventManager.start",
function () {
ok(EventManager._started, "EventManager was started again.");
ok(true, "EventManager was started again.");
ok(AccessFu._enabled, "AccessFu was enabled again.");
AccessFuTest.finish();
});

Просмотреть файл

@ -3507,9 +3507,11 @@ let SessionStoreInternal = {
var _this = this;
aWindow.setTimeout(function() {
_this.restoreDimensions.apply(_this, [aWindow, aWinData.width || 0,
aWinData.height || 0, "screenX" in aWinData ? aWinData.screenX : NaN,
"screenY" in aWinData ? aWinData.screenY : NaN,
_this.restoreDimensions.apply(_this, [aWindow,
+aWinData.width || 0,
+aWinData.height || 0,
"screenX" in aWinData ? +aWinData.screenX : NaN,
"screenY" in aWinData ? +aWinData.screenY : NaN,
aWinData.sizemode || "", aWinData.sidebar || ""]);
}, 0);
},

Просмотреть файл

@ -148,6 +148,7 @@ _BROWSER_FILES = \
browser_tabview_snapping.js \
browser_tabview_startup_transitions.js \
browser_tabview_undo_group.js \
browser_tabview_bug610242.js \
dummy_page.html \
head.js \
search1.html \

Просмотреть файл

@ -3700,7 +3700,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
}
#social-provider-button {
-moz-image-region: rect(0, 16px, 16px, 0);
list-style-image: url(chrome://browser/skin/social/services-16.png);
}
@ -3708,6 +3707,9 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
#social-provider-button {
list-style-image: url(chrome://browser/skin/social/services-16@2x.png);
}
#social-provider-button > .toolbarbutton-icon {
width: 16px;
}
}
#social-provider-button > .toolbarbutton-menu-dropmarker {

Просмотреть файл

@ -11,7 +11,6 @@ class TestBuildDict(unittest.TestCase):
"""
Test that missing required values raises.
"""
self.assertRaises(Exception, build_dict, {})
self.assertRaises(Exception, build_dict, {'OS_TARGET':'foo'})
self.assertRaises(Exception, build_dict, {'TARGET_CPU':'foo'})
self.assertRaises(Exception, build_dict, {'MOZ_WIDGET_TOOLKIT':'foo'})

Просмотреть файл

@ -15,35 +15,38 @@ import re
import sys
import json
import buildconfig
def build_dict(env=os.environ):
def build_dict(env=None):
"""
Build a dict containing data about the build configuration from
the environment.
"""
d = {}
substs = env or buildconfig.substs
env = env or os.environ
# Check that all required variables are present first.
required = ["TARGET_CPU", "OS_TARGET", "MOZ_WIDGET_TOOLKIT"]
missing = [r for r in required if r not in env]
missing = [r for r in required if r not in substs]
if missing:
raise Exception("Missing required environment variables: %s" %
', '.join(missing))
d = {}
d['topsrcdir'] = substs.get('TOPSRCDIR', buildconfig.topsrcdir)
if 'MOZCONFIG' in env:
mozconfig = env["MOZCONFIG"]
if 'TOPSRCDIR' in env:
mozconfig = os.path.join(env["TOPSRCDIR"], mozconfig)
d['mozconfig'] = os.path.normpath(mozconfig)
if 'TOPSRCDIR' in env:
d["topsrcdir"] = env["TOPSRCDIR"]
# os
o = env["OS_TARGET"]
o = substs["OS_TARGET"]
known_os = {"Linux": "linux",
"WINNT": "win",
"Darwin": "mac",
"Android": "b2g" if env["MOZ_WIDGET_TOOLKIT"] == "gonk" else "android"}
"Android": "b2g" if substs["MOZ_WIDGET_TOOLKIT"] == "gonk" else "android"}
if o in known_os:
d["os"] = known_os[o]
else:
@ -51,16 +54,16 @@ def build_dict(env=os.environ):
d["os"] = o.lower()
# Widget toolkit, just pass the value directly through.
d["toolkit"] = env["MOZ_WIDGET_TOOLKIT"]
d["toolkit"] = substs["MOZ_WIDGET_TOOLKIT"]
# Application name
if 'MOZ_APP_NAME' in env:
d["appname"] = env["MOZ_APP_NAME"]
if 'MOZ_APP_NAME' in substs:
d["appname"] = substs["MOZ_APP_NAME"]
# processor
p = env["TARGET_CPU"]
p = substs["TARGET_CPU"]
# for universal mac builds, put in a special value
if d["os"] == "mac" and "UNIVERSAL_BINARY" in env and env["UNIVERSAL_BINARY"] == "1":
if d["os"] == "mac" and "UNIVERSAL_BINARY" in substs and substs["UNIVERSAL_BINARY"] == "1":
p = "universal-x86-x86_64"
else:
# do some slight massaging for some values
@ -78,24 +81,22 @@ def build_dict(env=os.environ):
d["bits"] = 32
# other CPUs will wind up with unknown bits
# debug
d["debug"] = 'MOZ_DEBUG' in env and env['MOZ_DEBUG'] == '1'
d['debug'] = substs.get('MOZ_DEBUG') == '1'
d['crashreporter'] = substs.get('MOZ_CRASHREPORTER') == '1'
d['asan'] = substs.get('MOZ_ASAN') == '1'
d['tests_enabled'] = substs.get('ENABLE_TESTS') == "1"
d['bin_suffix'] = substs.get('BIN_SUFFIX', '')
# crashreporter
d["crashreporter"] = 'MOZ_CRASHREPORTER' in env and env['MOZ_CRASHREPORTER'] == '1'
# asan
d["asan"] = 'MOZ_ASAN' in env and env['MOZ_ASAN'] == '1'
return d
def write_json(file, env=os.environ):
def write_json(file, env=None):
"""
Write JSON data about the configuration specified in |env|
to |file|, which may be a filename or file-like object.
See build_dict for information about what environment variables are used,
and what keys are produced.
"""
build_conf = build_dict(env)
build_conf = build_dict(env=env)
if isinstance(file, basestring):
with open(file, "w") as f:
json.dump(build_conf, f)

Просмотреть файл

@ -4183,7 +4183,6 @@ ENABLE_SYSTEM_EXTENSION_DIRS=1
MOZ_BRANDING_DIRECTORY=
MOZ_OFFICIAL_BRANDING=
MOZ_FEEDS=1
MOZ_FLEXBOX=1
MOZ_WEBAPP_RUNTIME=
MOZ_JSDEBUGGER=1
MOZ_AUTH_EXTENSION=1
@ -6204,13 +6203,6 @@ if test -n "$MOZ_USE_NATIVE_POPUP_WINDOWS"; then
AC_DEFINE(MOZ_USE_NATIVE_POPUP_WINDOWS)
fi
dnl ========================================================
dnl CSS3 Flexbox Support
dnl ========================================================
if test -n "$MOZ_FLEXBOX"; then
AC_DEFINE(MOZ_FLEXBOX)
fi
dnl ========================================================
dnl Build Freetype in the tree
dnl ========================================================
@ -8812,7 +8804,6 @@ AC_SUBST(MOZ_NATIVE_JPEG)
AC_SUBST(MOZ_NATIVE_PNG)
AC_SUBST(MOZ_NATIVE_BZ2)
AC_SUBST(MOZ_FLEXBOX)
AC_SUBST(MOZ_JPEG_CFLAGS)
AC_SUBST(MOZ_JPEG_LIBS)
AC_SUBST(MOZ_BZ2_CFLAGS)

Просмотреть файл

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Text.h"
#include "nsTextNode.h"
namespace mozilla {
namespace dom {
@ -19,5 +20,18 @@ Text::SplitText(uint32_t aOffset, ErrorResult& rv)
return newChild.forget().downcast<Text>();
}
/* static */ already_AddRefed<Text>
Text::Constructor(const GlobalObject& aGlobal, const nsAString& aData,
ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
if (!window || !window->GetDoc()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return window->GetDoc()->CreateTextNode(aData);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -27,6 +27,10 @@ public:
{
rv = GetWholeText(aWholeText);
}
static already_AddRefed<Text>
Constructor(const GlobalObject& aGlobal, const nsAString& aData,
ErrorResult& aRv);
};
} // namespace dom

Просмотреть файл

@ -2346,6 +2346,16 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
}
#endif
#ifdef DEBUG
{
uint32_t appId;
nsresult rv = NodePrincipal()->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
"Document should never have UNKNOWN_APP_ID");
}
#endif
MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
"Bad readyState");
SetReadyStateInternal(READYSTATE_LOADING);

Просмотреть файл

@ -3077,3 +3077,15 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
::InvalidateAllFrames(commonAncestor);
}
}
/* static */ already_AddRefed<nsRange>
nsRange::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
if (!window || !window->GetDoc()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return window->GetDoc()->CreateRange(aRv);
}

Просмотреть файл

@ -164,6 +164,10 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
// WebIDL
static already_AddRefed<nsRange>
Constructor(const mozilla::dom::GlobalObject& global,
mozilla::ErrorResult& aRv);
bool Collapsed() const
{
return mIsPositioned && mStartParent == mEndParent &&

Просмотреть файл

@ -626,6 +626,8 @@ MOCHITEST_FILES_C= \
badMessageEvent2.eventsource^headers^ \
test_object.html \
test_bug869006.html \
test_bug868999.html \
test_bug869000.html \
$(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec

Просмотреть файл

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=868999
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 868999</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=868999">Mozilla Bug 869006</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 868999 **/
var r = new Range();
ok(r, "Range has been created");
var doc = document.implementation.createDocument("", "", null);
var h1 = doc.createElement('h1');
doc.appendChild(h1);
var t = doc.createTextNode('Hello world');
h1.appendChild(t);
r.selectNodeContents(doc);
is(r.toString(), "Hello world", "new Range() works!");
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=869000
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 869000</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=869000">Mozilla Bug 869006</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 869000 **/
var c = new Text();
ok(c, "Text has been created without content");
is(c.data, "", "Text.data is ok");
c = new Text('foo');
ok(c, "Text has been created");
is(c.data, "foo", "Text.data is ok");
document.getElementById('display').appendChild(c);
ok(true, "Text has been added to the document");
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -198,6 +198,8 @@ WebGLContext::WebGLContext()
mIsScreenCleared = false;
mDisableFragHighP = false;
mDrawCallsSinceLastFlush = 0;
}
WebGLContext::~WebGLContext()
@ -806,6 +808,7 @@ public:
// Present our screenbuffer, if needed.
context->PresentScreenBuffer();
context->mDrawCallsSinceLastFlush = 0;
}
/** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,

Просмотреть файл

@ -1128,6 +1128,10 @@ protected:
ContextStatus mContextStatus;
bool mContextLostErrorSet;
// Used for some hardware (particularly Tegra 2 and 4) that likes to
// be Flushed while doing hundreds of draw calls.
int mDrawCallsSinceLastFlush;
int mAlreadyGeneratedWarnings;
bool mAlreadyWarnedAboutFakeVertexAttrib0;

Просмотреть файл

@ -38,6 +38,9 @@ using namespace mozilla::dom;
static bool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize);
static WebGLenum InternalFormatForFormatAndType(WebGLenum format, WebGLenum type, bool isGLES2);
// For a Tegra workaround.
static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
//
// WebGL API
//
@ -1477,6 +1480,17 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
mShouldPresent = true;
mIsScreenCleared = false;
}
if (gl->WorkAroundDriverBugs()) {
if (gl->Renderer() == gl::GLContext::RendererTegra) {
mDrawCallsSinceLastFlush++;
if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
gl->fFlush();
mDrawCallsSinceLastFlush = 0;
}
}
}
}
void
@ -1576,6 +1590,17 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
mShouldPresent = true;
mIsScreenCleared = false;
}
if (gl->WorkAroundDriverBugs()) {
if (gl->Renderer() == gl::GLContext::RendererTegra) {
mDrawCallsSinceLastFlush++;
if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
gl->fFlush();
mDrawCallsSinceLastFlush = 0;
}
}
}
}
void

Просмотреть файл

@ -448,7 +448,7 @@ public:
bool Muted() const
{
return mMuted;
return mMuted & MUTED_BY_CONTENT;
}
// XPCOM SetMuted() is OK
@ -786,9 +786,9 @@ protected:
void ProcessMediaFragmentURI();
/**
* Mute or unmute the audio, without changing the value that |muted| reports.
* Mute or unmute the audio and change the value that the |muted| map.
*/
void SetMutedInternal(bool aMuted);
void SetMutedInternal(uint32_t aMuted);
/**
* Suspend (if aPauseForInactiveDocument) or resume element playback and
@ -1009,8 +1009,13 @@ protected:
// 'Pause' method, or playback not yet having started.
WakeLockBoolWrapper mPaused;
// True if the sound is muted.
bool mMuted;
enum MutedReasons {
MUTED_BY_CONTENT = 0x01,
MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
MUTED_BY_AUDIO_CHANNEL = 0x04
};
uint32_t mMuted;
// True if the sound is being captured.
bool mAudioCaptured;
@ -1093,9 +1098,6 @@ protected:
// Audio Channel Type.
AudioChannelType mAudioChannelType;
// The audiochannel has been suspended.
bool mChannelSuspended;
// Is this media element playing?
bool mPlayingThroughTheAudioChannel;

Просмотреть файл

@ -129,7 +129,7 @@ HTMLAudioElement::MozSetup(uint32_t aChannels, uint32_t aRate, ErrorResult& aRv)
}
MetadataLoaded(aChannels, aRate, true, false, nullptr);
mAudioStream->SetVolume(mVolume);
mAudioStream->SetVolume(mMuted ? 0.0 : mVolume);
}
uint32_t
@ -248,11 +248,12 @@ HTMLAudioElement::CanPlayChanged(bool canPlay)
return HTMLMediaElement::CanPlayChanged(canPlay);
}
#ifdef MOZ_B2G
if (mChannelSuspended == !canPlay) {
return NS_OK;
if (canPlay) {
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
} else {
SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
}
mChannelSuspended = !canPlay;
SetMutedInternal(mChannelSuspended);
#endif
return NS_OK;
}
@ -299,6 +300,7 @@ HTMLAudioElement::UpdateAudioChannelPlayingState()
if (mPlayingThroughTheAudioChannel) {
bool canPlay;
mAudioChannelAgent->StartPlaying(&canPlay);
CanPlayChanged(canPlay);
} else {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;

Просмотреть файл

@ -1506,15 +1506,8 @@ HTMLMediaElement::SetVolume(double aVolume, ErrorResult& aRv)
mVolume = aVolume;
if (!mMuted) {
if (mDecoder) {
mDecoder->SetVolume(mVolume);
} else if (mAudioStream) {
mAudioStream->SetVolume(mVolume);
} else if (mSrcStream) {
GetSrcMediaStream()->SetAudioOutputVolume(this, float(mVolume));
}
}
// Here we want just to update the volume.
SetMutedInternal(mMuted);
DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
}
@ -1683,9 +1676,16 @@ NS_IMETHODIMP HTMLMediaElement::GetMuted(bool* aMuted)
return NS_OK;
}
void HTMLMediaElement::SetMutedInternal(bool aMuted)
void HTMLMediaElement::SetMutedInternal(uint32_t aMuted)
{
float effectiveVolume = aMuted ? 0.0f : float(mVolume);
uint32_t oldMuted = mMuted;
mMuted = aMuted;
if (!!aMuted == !!oldMuted) {
return;
}
float effectiveVolume = mMuted ? 0.0f : float(mVolume);
if (mDecoder) {
mDecoder->SetVolume(effectiveVolume);
@ -1698,11 +1698,11 @@ void HTMLMediaElement::SetMutedInternal(bool aMuted)
NS_IMETHODIMP HTMLMediaElement::SetMuted(bool aMuted)
{
if (aMuted == mMuted)
return NS_OK;
mMuted = aMuted;
SetMutedInternal(aMuted);
if (aMuted) {
SetMutedInternal(mMuted | MUTED_BY_CONTENT);
} else {
SetMutedInternal(mMuted & ~MUTED_BY_CONTENT);
}
DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
return NS_OK;
@ -1910,7 +1910,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
mAutoplaying(true),
mAutoplayEnabled(true),
mPaused(true),
mMuted(false),
mMuted(0),
mAudioCaptured(false),
mPlayingBeforeSeek(false),
mPausedForInactiveDocumentOrChannel(false),
@ -1932,7 +1932,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
mHasAudio(false),
mDownloadSuspendedByCache(false),
mAudioChannelType(AUDIO_CHANNEL_NORMAL),
mChannelSuspended(false),
mPlayingThroughTheAudioChannel(false)
{
#ifdef PR_LOGGING
@ -2238,8 +2237,9 @@ bool HTMLMediaElement::CheckAudioChannelPermissions(const nsAString& aString)
void HTMLMediaElement::DoneCreatingElement()
{
if (HasAttr(kNameSpaceID_None, nsGkAtoms::muted))
mMuted = true;
if (HasAttr(kNameSpaceID_None, nsGkAtoms::muted)) {
mMuted |= MUTED_BY_CONTENT;
}
}
bool HTMLMediaElement::IsHTMLFocusable(bool aWithMouse,
@ -3067,7 +3067,8 @@ bool HTMLMediaElement::CanActivateAutoplay()
// For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
// this element itself might be blocking the stream from making progress by
// being paused.
return mAutoplaying &&
return !mPausedForInactiveDocumentOrChannel &&
mAutoplaying &&
mPaused &&
(mDownloadSuspendedByCache ||
(mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
@ -3278,13 +3279,14 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
{
nsIDocument* ownerDoc = OwnerDoc();
// SetVisibilityState will update mChannelSuspended via the CanPlayChanged callback.
// SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
// CanPlayChanged callback.
if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&
mAudioChannelAgent) {
mAudioChannelAgent->SetVisibilityState(!ownerDoc->Hidden());
}
bool suspendEvents = !ownerDoc->IsActive() || !ownerDoc->IsVisible();
bool pauseElement = suspendEvents || mChannelSuspended;
bool pauseElement = suspendEvents || (mMuted & MUTED_BY_AUDIO_CHANNEL);
SuspendOrResumeElement(pauseElement, suspendEvents);
@ -3661,14 +3663,12 @@ HTMLMediaElement::SetPlaybackRate(double aPlaybackRate, ErrorResult& aRv)
mPlaybackRate = ClampPlaybackRate(aPlaybackRate);
if (!mMuted) {
if (mPlaybackRate < 0 ||
mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
mPlaybackRate < THRESHOLD_LOW_PLAYBACKRATE_AUDIO) {
SetMutedInternal(true);
SetMutedInternal(mMuted | MUTED_BY_INVALID_PLAYBACK_RATE);
} else {
SetMutedInternal(false);
}
SetMutedInternal(mMuted & ~MUTED_BY_INVALID_PLAYBACK_RATE);
}
if (mDecoder) {
@ -3712,16 +3712,16 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(bool aCanPlay)
return NS_OK;
}
// We have to mute this channel:
if (!aCanPlay && !mChannelSuspended) {
mChannelSuspended = true;
// We have to mute this channel.
if (!aCanPlay && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
} else if (aCanPlay && mChannelSuspended) {
mChannelSuspended = false;
} else if (aCanPlay && (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
}
SuspendOrResumeElement(mChannelSuspended, false);
SuspendOrResumeElement(mMuted & MUTED_BY_AUDIO_CHANNEL, false);
return NS_OK;
}
@ -3753,7 +3753,7 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState()
if (mPlayingThroughTheAudioChannel) {
bool canPlay;
mAudioChannelAgent->StartPlaying(&canPlay);
mPaused.SetCanPlay(canPlay);
CanPlayChanged(canPlay);
} else {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;

Просмотреть файл

@ -19,11 +19,11 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptProcessorNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptProcessorNode)
if (tmp->Context()) {
tmp->Context()->UnregisterScriptProcessorNode(tmp);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScriptProcessorNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

Просмотреть файл

@ -48,6 +48,8 @@ WMFReader::WMFReader(AbstractMediaDecoder* aDecoder)
mVideoWidth(0),
mVideoHeight(0),
mVideoStride(0),
mAudioFrameSum(0),
mAudioFrameOffset(0),
mHasAudio(false),
mHasVideo(false),
mCanSeek(false),
@ -605,6 +607,31 @@ GetSampleDuration(IMFSample* aSample)
return HNsToUsecs(duration);
}
HRESULT
HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames)
{
MOZ_ASSERT(aOutFrames);
const int64_t HNS_PER_S = USECS_PER_S * 10;
CheckedInt<int64_t> i = aHNs;
i *= aRate;
i /= HNS_PER_S;
NS_ENSURE_TRUE(i.isValid(), E_FAIL);
*aOutFrames = i.value();
return S_OK;
}
HRESULT
FramesToUsecs(int64_t aSamples, uint32_t aRate, int64_t* aOutUsecs)
{
MOZ_ASSERT(aOutUsecs);
CheckedInt<int64_t> i = aSamples;
i *= USECS_PER_S;
i /= aRate;
NS_ENSURE_TRUE(i.isValid(), E_FAIL);
*aOutUsecs = i.value();
return S_OK;
}
bool
WMFReader::DecodeAudioData()
{
@ -660,11 +687,32 @@ WMFReader::DecodeAudioData()
memcpy(pcmSamples.get(), data, currentLength);
buffer->Unlock();
int64_t offset = mDecoder->GetResource()->Tell();
int64_t timestamp = HNsToUsecs(timestampHns);
int64_t duration = GetSampleDuration(sample);
// We calculate the timestamp and the duration based on the number of audio
// frames we've already played. We don't trust the timestamp stored on the
// IMFSample, as sometimes it's wrong, possibly due to buggy encoders?
mAudioQueue.Push(new AudioData(offset,
// If this sample block comes after a discontinuity (i.e. a gap or seek)
// reset the frame counters, and capture the timestamp. Future timestamps
// will be offset from this block's timestamp.
UINT32 discontinuity = false;
sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity);
if (discontinuity) {
mAudioFrameSum = 0;
hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
}
int64_t timestamp;
hr = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate, &timestamp);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mAudioFrameSum += numFrames;
int64_t duration;
hr = FramesToUsecs(numFrames, mAudioRate, &duration);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
timestamp,
duration,
numFrames,

Просмотреть файл

@ -91,6 +91,13 @@ private:
uint32_t mVideoHeight;
uint32_t mVideoStride;
// The offset, in audio frames, at which playback started since the
// last discontinuity.
int64_t mAudioFrameOffset;
// The number of audio frames that we've played since the last
// discontinuity.
int64_t mAudioFrameSum;
bool mHasAudio;
bool mHasVideo;
bool mCanSeek;

Просмотреть файл

@ -13,8 +13,8 @@ namespace mozilla {
namespace dom {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGAnimatedRect)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedRect, mSVGElement)

Просмотреть файл

@ -92,6 +92,7 @@ MOCHITEST_FILES = \
test_viewport.html \
zoom-helper.svg \
test_zoom.xhtml \
test_bug872812.html \
$(NULL)
ifneq (android,$(MOZ_WIDGET_TOOLKIT))

Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=872812
-->
<head>
<title>Test for Bug 872812</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=872812">Mozilla Bug 872812</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<iframe id="svg" src="viewport-helper.svg"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
ok(svg, "SVG exists");
var a = document.createEvent('CustomEvent').initCustomEvent('', '', '', svg.viewBox);
ok(true, "CustomEvent exists and we are not crashed!");
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -4983,10 +4983,11 @@ nsLocationSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
NS_IMETHODIMP
nsLocationSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsid id, jsval *vp, bool *_retval)
JSObject *obj, jsid aId, jsval *vp, bool *_retval)
{
// Shadowing protection. This will go away when nsLocation moves to the new
// bindings.
JS::Rooted<jsid> id(cx, aId);
if (wrapper->HasNativeMember(id)) {
JS_ReportError(cx, "Permission denied to shadow native property");
return NS_ERROR_FAILURE;

Просмотреть файл

@ -213,7 +213,7 @@ nsFocusManager::Observe(nsISupports *aSubject,
if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsDependentString data(aData);
if (data.EqualsLiteral("accessibility.browsewithcaret")) {
UpdateCaret(false, true, mFocusedContent);
UpdateCaretForCaretBrowsingMode();
}
else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
nsIContent::sTabFocusModelAppliesToXUL =
@ -1994,6 +1994,12 @@ nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
#endif
}
void
nsFocusManager::UpdateCaretForCaretBrowsingMode()
{
UpdateCaret(false, true, mFocusedContent);
}
void
nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
bool aUpdateVisibility,
@ -2139,6 +2145,8 @@ nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
// First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
caret->SetCaretVisible(false);
// Caret must blink on non-editable elements
caret->SetIgnoreUserModify(true);
// Tell the caret which selection to use
caret->SetCaretDOMSelection(domSelection);
@ -2150,6 +2158,7 @@ nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
if (!selCon)
return NS_ERROR_FAILURE;
selCon->SetCaretReadOnly(false);
selCon->SetCaretEnabled(aVisible);
caret->SetCaretVisible(aVisible);
}

Просмотреть файл

@ -84,6 +84,11 @@ public:
mMouseDownEventHandlingDocument = aDocument;
}
/**
* Update the caret with current mode (whether in caret browsing mode or not).
*/
void UpdateCaretForCaretBrowsingMode();
/**
* Returns the content node that would be focused if aWindow was in an
* active window. This will traverse down the frame hierarchy, starting at

Просмотреть файл

@ -6896,16 +6896,18 @@ nsGlobalWindow::PostMessageMoz(const JS::Value& aMessage,
class nsCloseEvent : public nsRunnable {
nsRefPtr<nsGlobalWindow> mWindow;
bool mIndirect;
nsCloseEvent(nsGlobalWindow *aWindow)
nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
: mWindow(aWindow)
, mIndirect(aIndirect)
{}
public:
static nsresult
PostCloseEvent(nsGlobalWindow* aWindow) {
nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow);
PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
nsresult rv = NS_DispatchToCurrentThread(ev);
if (NS_SUCCEEDED(rv))
aWindow->MaybeForgiveSpamCount();
@ -6913,8 +6915,12 @@ public:
}
NS_IMETHOD Run() {
if (mWindow)
if (mWindow) {
if (mIndirect) {
return PostCloseEvent(mWindow, false);
}
mWindow->ReallyCloseWindow();
}
return NS_OK;
}
@ -7047,23 +7053,29 @@ nsGlobalWindow::FinalClose()
// Flag that we were closed.
mIsClosed = true;
JSContext *cx = nsContentUtils::GetCurrentJSContext();
if (cx) {
nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx);
if (currentCX && currentCX == GetContextInternal()) {
currentCX->SetTerminationFunction(CloseWindow, this);
mHavePendingClose = true;
return NS_OK;
}
}
// We may have plugins on the page that have issued this close from their
// event loop and because we currently destroy the plugin window with
// frames, we crash. So, if we are called from Javascript, post an event
// to really close the window.
if (nsContentUtils::IsCallerChrome() ||
NS_FAILED(nsCloseEvent::PostCloseEvent(this))) {
// This stuff is non-sensical but incredibly fragile. The reasons for the
// behavior here don't make sense today and may not have ever made sense,
// but various bits of frontend code break when you change them. If you need
// to fix up this behavior, feel free to. It's a righteous task, but involves
// wrestling with various download manager tests, frontend code, and possible
// broken addons. The chrome tests in toolkit/mozapps/downloads are a good
// testing ground.
//
// Here are some quirks that the test suite depends on:
//
// * When chrome code executes |win|.close(), that close happens immediately,
// along with the accompanying "domwindowclosed" notification. But _only_ if
// |win|'s JSContext is not at the top of the stack. If it is, the close
// _must not_ happen immediately.
//
// * If |win|'s JSContext is at the top of the stack, we must complete _two_
// round-trips to the event loop before the call to ReallyCloseWindow. This
// allows setTimeout handlers that are set after FinalClose() is called to
// run before the window is torn down.
bool indirect = nsContentUtils::GetCurrentJSContext() ==
GetContextInternal()->GetNativeContext();
if ((!indirect && nsContentUtils::IsCallerChrome()) ||
NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
ReallyCloseWindow();
} else {
mHavePendingClose = true;
@ -9714,15 +9726,25 @@ nsGlobalWindow::GetParentInternal()
return NULL;
}
// static
void
nsGlobalWindow::CloseBlockScriptTerminationFunc(nsISupports *aRef)
nsGlobalWindow::UnblockScriptedClosing()
{
nsGlobalWindow* pwin = static_cast<nsGlobalWindow*>
(static_cast<nsPIDOMWindow*>(aRef));
pwin->mBlockScriptedClosingFlag = false;
mBlockScriptedClosingFlag = false;
}
class AutoUnblockScriptClosing
{
private:
nsRefPtr<nsGlobalWindow> mWin;
public:
AutoUnblockScriptClosing(nsGlobalWindow *aWin) : mWin(aWin) {};
~AutoUnblockScriptClosing()
{
void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run));
};
};
nsresult
nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, bool aDialog,
@ -9752,6 +9774,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript,
"Shouldn't have caller context when called noscript");
mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
// Calls to window.open from script should navigate.
MOZ_ASSERT(aCalledNoScript || aNavigate);
@ -9813,8 +9837,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
// so that whatever popup blocker UI the app has will be visible.
if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) {
mBlockScriptedClosingFlag = true;
mContext->SetTerminationFunction(CloseBlockScriptTerminationFunc,
this);
closeUnblocker.construct(this);
}
}
@ -9918,22 +9941,6 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
return rv;
}
// static
void
nsGlobalWindow::CloseWindow(nsISupports *aWindow)
{
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
nsGlobalWindow* globalWin =
static_cast<nsGlobalWindow *>
(static_cast<nsPIDOMWindow*>(win));
// Need to post an event for closing, otherwise window and
// presshell etc. may get destroyed while creating frames, bug 338897.
nsCloseEvent::PostCloseEvent(globalWin);
// else if OOM, better not to close. That might cause a crash.
}
//*****************************************************************************
// nsGlobalWindow: Timeout Functions
//*****************************************************************************

Просмотреть файл

@ -580,11 +580,12 @@ public:
nsresult Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData);
void UnblockScriptedClosing();
static void Init();
static void ShutDown();
static void CleanupCachedXBLHandlers(nsGlobalWindow* aWindow);
static bool IsCallerChrome();
static void CloseBlockScriptTerminationFunc(nsISupports *aRef);
static void RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
nsGlobalWindow *aWindow);
@ -903,8 +904,6 @@ protected:
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn);
static void CloseWindow(nsISupports* aWindow);
// Timeout Functions
// Language agnostic timeout function (all args passed).
// |interval| is in milliseconds.

Просмотреть файл

@ -27,11 +27,9 @@ class nsIScriptObjectPrincipal;
class nsIDOMWindow;
class nsIURI;
typedef void (*nsScriptTerminationFunc)(nsISupports* aRef);
#define NS_ISCRIPTCONTEXT_IID \
{ 0x821c5be9, 0xbf9e, 0x4041, \
{ 0x9f, 0xf2, 0x1f, 0xca, 0x39, 0xf7, 0x89, 0xf3 } }
{ 0xef0c91ce, 0x14f6, 0x41c9, \
{ 0xa5, 0x77, 0xa6, 0xeb, 0xdc, 0x6d, 0x44, 0x7b } }
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
know what language we have is a little silly... */
@ -179,11 +177,10 @@ public:
* A GC may be done if "necessary."
* This call is necessary if script evaluation is done
* without using the EvaluateScript method.
* @param aTerminated If true then call termination function if it was
* previously set. Within DOM this will always be true, but outside
* callers (such as xpconnect) who may do script evaluations nested
* inside DOM script evaluations can pass false to avoid premature
* calls to the termination function.
* @param aTerminated If true then do script termination handling. Within DOM
* this will always be true, but outside callers (such as xpconnect) who
* may do script evaluations nested inside inside DOM script evaluations
* can pass false to avoid premature termination handling.
* @return NS_OK if the method is successful
*/
virtual void ScriptEvaluated(bool aTerminated) = 0;
@ -196,19 +193,6 @@ public:
virtual nsresult Deserialize(nsIObjectInputStream* aStream,
JS::MutableHandle<JSScript*> aResult) = 0;
/**
* JS only - this function need not be implemented by languages other
* than JS (ie, this should be moved to a private interface!)
* Called to specify a function that should be called when the current
* script (if there is one) terminates. Generally used if breakdown
* of script state needs to happen, but should be deferred till
* the end of script evaluation.
*
* @throws NS_ERROR_OUT_OF_MEMORY if that happens
*/
virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
nsIDOMWindow* aRef) = 0;
/**
* Called to disable/enable script execution in this context.
*/

Просмотреть файл

@ -1141,7 +1141,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime, bool aGCOnDestruction,
::JS_SetOperationCallback(mContext, DOMOperationCallback);
}
mIsInitialized = false;
mTerminations = nullptr;
mScriptsEnabled = true;
mOperationCallbackTime = 0;
mModalStateTime = 0;
@ -1156,11 +1155,6 @@ nsJSContext::~nsJSContext()
mNext->mPrev = mPrev;
}
// We may still have pending termination functions if the context is destroyed
// before they could be executed. In this case, free the references to their
// parameters, but don't execute the functions (see bug 622326).
delete mTerminations;
mGlobalObjectRef = nullptr;
DestroyJSContext();
@ -1289,8 +1283,6 @@ nsJSContext::EvaluateString(const nsAString& aScript,
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(ok, NS_OK);
nsJSContext::TerminationFuncHolder holder(this);
// Scope the JSAutoCompartment so that it gets destroyed before we pop the
// cx and potentially call JS_RestoreFrameChain.
XPCAutoRequest ar(mContext);
@ -1414,7 +1406,6 @@ nsJSContext::ExecuteScript(JSScript* aScriptObject_,
nsCxPusher pusher;
pusher.Push(mContext);
nsJSContext::TerminationFuncHolder holder(this);
XPCAutoRequest ar(mContext);
// Scope the JSAutoCompartment so that it gets destroyed before we pop the
@ -2361,20 +2352,6 @@ nsJSContext::IsContextInitialized()
void
nsJSContext::ScriptEvaluated(bool aTerminated)
{
if (aTerminated && mTerminations) {
// Make sure to null out mTerminations before doing anything that
// might cause new termination funcs to be added!
nsJSContext::TerminationFuncClosure* start = mTerminations;
mTerminations = nullptr;
for (nsJSContext::TerminationFuncClosure* cur = start;
cur;
cur = cur->mNext) {
(*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
}
delete start;
}
JS_MaybeGC(mContext);
if (aTerminated) {
@ -2384,17 +2361,6 @@ nsJSContext::ScriptEvaluated(bool aTerminated)
}
}
void
nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
nsIDOMWindow* aRef)
{
NS_PRECONDITION(GetExecutingScript(), "should be executing script");
nsJSContext::TerminationFuncClosure* newClosure =
new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
mTerminations = newClosure;
}
bool
nsJSContext::GetScriptsEnabled()
{

Просмотреть файл

@ -75,8 +75,6 @@ public:
virtual bool IsContextInitialized();
virtual void ScriptEvaluated(bool aTerminated);
virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
nsIDOMWindow* aRef);
virtual bool GetScriptsEnabled();
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
@ -186,66 +184,6 @@ private:
JSContext *mContext;
bool mActive;
// Public so we can use it from CallbackFunction
public:
struct TerminationFuncHolder;
protected:
friend struct TerminationFuncHolder;
struct TerminationFuncClosure
{
TerminationFuncClosure(nsScriptTerminationFunc aFunc,
nsISupports* aArg,
TerminationFuncClosure* aNext) :
mTerminationFunc(aFunc),
mTerminationFuncArg(aArg),
mNext(aNext)
{
}
~TerminationFuncClosure()
{
delete mNext;
}
nsScriptTerminationFunc mTerminationFunc;
nsCOMPtr<nsISupports> mTerminationFuncArg;
TerminationFuncClosure* mNext;
};
// Public so we can use it from CallbackFunction
public:
struct TerminationFuncHolder
{
TerminationFuncHolder(nsJSContext* aContext)
: mContext(aContext),
mTerminations(aContext->mTerminations)
{
aContext->mTerminations = nullptr;
}
~TerminationFuncHolder()
{
// Have to be careful here. mContext might have picked up new
// termination funcs while the script was evaluating. Prepend whatever
// we have to the current termination funcs on the context (since our
// termination funcs were posted first).
if (mTerminations) {
TerminationFuncClosure* cur = mTerminations;
while (cur->mNext) {
cur = cur->mNext;
}
cur->mNext = mContext->mTerminations;
mContext->mTerminations = mTerminations;
}
}
nsJSContext* mContext;
TerminationFuncClosure* mTerminations;
};
protected:
TerminationFuncClosure* mTerminations;
private:
bool mIsInitialized;
bool mScriptsEnabled;
bool mGCOnDestruction;

Просмотреть файл

@ -314,6 +314,65 @@ public:
{}
};
// A specialization of Optional for JSObject* to make sure that when someone
// calls Construct() on it we will pre-initialized the JSObject* to nullptr so
// it can be traced safely.
template<>
class Optional<JSObject*> : public Optional_base<JSObject*, JSObject*>
{
public:
Optional() :
Optional_base<JSObject*, JSObject*>()
{}
explicit Optional(JSObject* aValue) :
Optional_base<JSObject*, JSObject*>(aValue)
{}
// Don't allow us to have an uninitialized JSObject*
void Construct()
{
// The Android compiler sucks and thinks we're trying to construct
// a JSObject* from an int if we don't cast here. :(
Optional_base<JSObject*, JSObject*>::Construct(
static_cast<JSObject*>(nullptr));
}
template <class T1>
void Construct(const T1& t1)
{
Optional_base<JSObject*, JSObject*>::Construct(t1);
}
};
// A specialization of Optional for JS::Value to make sure that when someone
// calls Construct() on it we will pre-initialized the JS::Value to
// JS::UndefinedValue() so it can be traced safely.
template<>
class Optional<JS::Value> : public Optional_base<JS::Value, JS::Value>
{
public:
Optional() :
Optional_base<JS::Value, JS::Value>()
{}
explicit Optional(JS::Value aValue) :
Optional_base<JS::Value, JS::Value>(aValue)
{}
// Don't allow us to have an uninitialized JS::Value
void Construct()
{
Optional_base<JS::Value, JS::Value>::Construct(JS::UndefinedValue());
}
template <class T1>
void Construct(const T1& t1)
{
Optional_base<JS::Value, JS::Value>::Construct(t1);
}
};
// Specialization for strings.
// XXXbz we can't pull in FakeDependentString here, because it depends on
// internal strings. So we just have to forward-declare it and reimplement its

Просмотреть файл

@ -92,6 +92,11 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
// Victory! We have a JSContext. Now do the things we need a JSContext for.
mAr.construct(cx);
// And go ahead and stick our callable in a Rooted, to make sure it can't go
// gray again. We can do this even though we're not in the right compartment
// yet, because Rooted<> does not care about compartments.
mRootedCallable.construct(cx, aCallback);
// Make sure our JSContext is pushed on the stack.
mCxPusher.Push(cx);
@ -109,14 +114,6 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
nsresult rv = nsContentUtils::GetSecurityManager()->
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
// Construct a termination func holder even if we're not planning to
// run any script. We need this because we're going to call
// ScriptEvaluated even if we don't run the script... See XXX
// comment above.
if (ctx) {
mTerminationFuncHolder.construct(static_cast<nsJSContext*>(ctx));
}
if (NS_FAILED(rv)) {
// Security check failed. We're done here.
return;

Просмотреть файл

@ -148,9 +148,10 @@ protected:
// this needs to be a Maybe.
Maybe<XPCAutoRequest> mAr;
// Can't construct a TerminationFuncHolder without an nsJSContext. But we
// generally want its destructor to come after the destructor of mCxPusher.
Maybe<nsJSContext::TerminationFuncHolder> mTerminationFuncHolder;
// We construct our JS::Rooted right after our JSAutoRequest; let's just
// hope that the change in ordering wrt the mCxPusher constructor here is
// ok.
Maybe<JS::Rooted<JSObject*> > mRootedCallable;
nsCxPusher mCxPusher;

Просмотреть файл

@ -7710,7 +7710,9 @@ class CGDictionary(CGThing):
selfName = self.makeClassName(d)
members = [ClassMember(self.makeMemberName(m[0].identifier.name),
self.getMemberType(m),
visibility="public") for m in self.memberInfo]
visibility="public",
body=self.getMemberInitializer(m))
for m in self.memberInfo]
ctor = ClassConstructor([], bodyInHeader=True, visibility="public")
methods = []
@ -7931,6 +7933,25 @@ class CGDictionary(CGThing):
return trace.define()
def getMemberInitializer(self, memberInfo):
"""
Get the right initializer for the member. Most members don't need one,
but we need to pre-initialize 'any' and 'object' that have a default
value, so they're safe to trace at all times.
"""
(member, _) = memberInfo
if not member.defaultValue:
# No default value means no need to set it up front, since it's
# inside an Optional and won't get traced until it's actually set
# up.
return None
type = member.type
if type.isAny():
return "JS::UndefinedValue()"
if type.isObject():
return "nullptr"
return None
@staticmethod
def makeIdName(name):
return name + "_id"

Просмотреть файл

@ -647,6 +647,7 @@ dictionary Dict : ParentDict {
object? anotherObj = null;
TestCallback? someCallback = null;
any someAny;
any anotherAny = null;
unrestricted float urFloat = 0;
unrestricted float urFloat2 = 1.1;

Просмотреть файл

@ -7,6 +7,7 @@ tail =
[test_add_put.js]
[test_add_twice_failure.js]
[test_advance.js]
[test_autoIncrement.js]
[test_autoIncrement_indexes.js]
[test_clear.js]
[test_complex_keyPaths.js]
@ -18,6 +19,7 @@ tail =
[test_cursor_update_updates_indexes.js]
[test_cursors.js]
[test_deleteDatabase.js]
[test_deleteDatabase_interactions.js]
[test_event_source.js]
[test_getAll.js]
[test_global_data.js]
@ -43,6 +45,7 @@ tail =
[test_overlapping_transactions.js]
[test_put_get_values.js]
[test_put_get_values_autoIncrement.js]
[test_readonly_transactions.js]
[test_remove_index.js]
[test_remove_objectStore.js]
[test_request_readyState.js]
@ -56,5 +59,7 @@ tail =
[test_transaction_lifetimes.js]
[test_transaction_lifetimes_nested.js]
[test_transaction_ordering.js]
[test_unique_index_update.js]
[test_writer_starvation.js]
# When adding files here please also update test/unit/xpcshell.ini!

Просмотреть файл

@ -9,392 +9,9 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function genCheck(key, value, test, options) {
return function(event) {
is(JSON.stringify(event.target.result), JSON.stringify(key),
"correct returned key in " + test);
if (options && options.store) {
is(event.target.source, options.store, "correct store in " + test);
}
if (options && options.trans) {
is(event.target.transaction, options.trans, "correct transaction in " + test);
}
event.target.source.get(key).onsuccess = function(event) {
is(JSON.stringify(event.target.result), JSON.stringify(value),
"correct stored value in " + test);
continueToNextStepSync();
}
}
}
function testSteps()
{
const dbname = window.location.pathname;
const RW = IDBTransaction.READ_WRITE
let c1 = 1;
let c2 = 1;
let openRequest = indexedDB.open(dbname, 1);
openRequest.onerror = errorHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
let event = yield;
let db = event.target.result;
let trans = event.target.transaction;
// Create test stores
let store1 = db.createObjectStore("store1", { autoIncrement: true });
let store2 = db.createObjectStore("store2", { autoIncrement: true, keyPath: "id" });
let store3 = db.createObjectStore("store3", { autoIncrement: false });
is(store1.autoIncrement, true, "store1 .autoIncrement");
is(store2.autoIncrement, true, "store2 .autoIncrement");
is(store3.autoIncrement, false, "store3 .autoIncrement");
store1.createIndex("unique1", "unique", { unique: true });
store2.createIndex("unique1", "unique", { unique: true });
// Test simple inserts
let test = " for test simple insert"
store1.add({ foo: "value1" }).onsuccess =
genCheck(c1++, { foo: "value1" }, "first" + test);
store1.add({ foo: "value2" }).onsuccess =
genCheck(c1++, { foo: "value2" }, "second" + test);
yield;
yield;
store2.put({ bar: "value1" }).onsuccess =
genCheck(c2, { bar: "value1", id: c2 }, "first in store2" + test,
{ store: store2 });
c2++;
store1.put({ foo: "value3" }).onsuccess =
genCheck(c1++, { foo: "value3" }, "third" + test,
{ store: store1 });
yield;
yield;
store2.get(IDBKeyRange.lowerBound(c2)).onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, undefined, "no such value" + test);
// Close version_change transaction
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target, openRequest, "succeeded to open" + test);
is(event.type, "success", "succeeded to open" + test);
// Test inserting explicit keys
test = " for test explicit keys";
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 1 }, 100).onsuccess =
genCheck(100, { explicit: 1 }, "first" + test);
c1 = 101;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 2 }).onsuccess =
genCheck(c1++, { explicit: 2 }, "second" + test);
yield; yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 3 }, 200).onsuccess =
genCheck(200, { explicit: 3 }, "third" + test);
c1 = 201;
trans.objectStore("store1").add({ explicit: 4 }).onsuccess =
genCheck(c1++, { explicit: 4 }, "fourth" + test);
yield; yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 5 }, 150).onsuccess =
genCheck(150, { explicit: 5 }, "fifth" + test);
yield;
trans.objectStore("store1").add({ explicit: 6 }).onsuccess =
genCheck(c1++, { explicit: 6 }, "sixth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 7 }, "key").onsuccess =
genCheck("key", { explicit: 7 }, "seventh" + test);
yield;
trans.objectStore("store1").add({ explicit: 8 }).onsuccess =
genCheck(c1++, { explicit: 8 }, "eighth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 7 }, [100000]).onsuccess =
genCheck([100000], { explicit: 7 }, "seventh" + test);
yield;
trans.objectStore("store1").add({ explicit: 8 }).onsuccess =
genCheck(c1++, { explicit: 8 }, "eighth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 9 }, -100000).onsuccess =
genCheck(-100000, { explicit: 9 }, "ninth" + test);
yield;
trans.objectStore("store1").add({ explicit: 10 }).onsuccess =
genCheck(c1++, { explicit: 10 }, "tenth" + test);
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 1, id: 300 }).onsuccess =
genCheck(300, { explicit2: 1, id: 300 }, "first store2" + test);
c2 = 301;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 2 }).onsuccess =
genCheck(c2, { explicit2: 2, id: c2 }, "second store2" + test);
c2++;
yield; yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 3, id: 400 }).onsuccess =
genCheck(400, { explicit2: 3, id: 400 }, "third store2" + test);
c2 = 401;
trans.objectStore("store2").add({ explicit2: 4 }).onsuccess =
genCheck(c2, { explicit2: 4, id: c2 }, "fourth store2" + test);
c2++;
yield; yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 5, id: 150 }).onsuccess =
genCheck(150, { explicit: 5, id: 150 }, "fifth store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 6 }).onsuccess =
genCheck(c2, { explicit: 6, id: c2 }, "sixth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 7, id: "key" }).onsuccess =
genCheck("key", { explicit: 7, id: "key" }, "seventh store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 8 }).onsuccess =
genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 7, id: [100000] }).onsuccess =
genCheck([100000], { explicit: 7, id: [100000] }, "seventh store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 8 }).onsuccess =
genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 9, id: -100000 }).onsuccess =
genCheck(-100000, { explicit: 9, id: -100000 }, "ninth store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 10 }).onsuccess =
genCheck(c2, { explicit: 10, id: c2 }, "tenth store2" + test);
c2++;
yield;
// Test separate transactions doesn't generate overlapping numbers
test = " for test non-overlapping counts";
trans = db.transaction("store1", RW);
trans2 = db.transaction("store1", RW);
trans2.objectStore("store1").put({ over: 2 }).onsuccess =
genCheck(c1 + 1, { over: 2 }, "first" + test,
{ trans: trans2 });
trans.objectStore("store1").put({ over: 1 }).onsuccess =
genCheck(c1, { over: 1 }, "second" + test,
{ trans: trans });
c1 += 2;
yield; yield;
trans = db.transaction("store2", RW);
trans2 = db.transaction("store2", RW);
trans2.objectStore("store2").put({ over: 2 }).onsuccess =
genCheck(c2 + 1, { over: 2, id: c2 + 1 }, "third" + test,
{ trans: trans2 });
trans.objectStore("store2").put({ over: 1 }).onsuccess =
genCheck(c2, { over: 1, id: c2 }, "fourth" + test,
{ trans: trans });
c2 += 2;
yield; yield;
// Test that error inserts doesn't increase generator
test = " for test error inserts";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ unique: 1 }, -1);
trans.objectStore("store2").add({ unique: 1, id: "unique" });
trans.objectStore("store1").add({ error: 1, unique: 1 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store1").add({ error: 2 }).onsuccess =
genCheck(c1++, { error: 2 }, "first" + test);
yield; yield;
trans.objectStore("store2").add({ error: 3, unique: 1 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store2").add({ error: 4 }).onsuccess =
genCheck(c2, { error: 4, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").add({ error: 5, unique: 1 }, 100000).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store1").add({ error: 6 }).onsuccess =
genCheck(c1++, { error: 6 }, "third" + test);
yield; yield;
trans.objectStore("store2").add({ error: 7, unique: 1, id: 100000 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store2").add({ error: 8 }).onsuccess =
genCheck(c2, { error: 8, id: c2 }, "fourth" + test);
c2++;
yield; yield;
// Test that aborts doesn't increase generator
test = " for test aborted transaction";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ abort: 1 }).onsuccess =
genCheck(c1, { abort: 1 }, "first" + test);
trans.objectStore("store2").put({ abort: 2 }).onsuccess =
genCheck(c2, { abort: 2, id: c2 }, "second" + test);
yield; yield;
trans.objectStore("store1").add({ abort: 3 }, 500).onsuccess =
genCheck(500, { abort: 3 }, "third" + test);
trans.objectStore("store2").put({ abort: 4, id: 600 }).onsuccess =
genCheck(600, { abort: 4, id: 600 }, "fourth" + test);
yield; yield;
trans.objectStore("store1").add({ abort: 5 }).onsuccess =
genCheck(501, { abort: 5 }, "fifth" + test);
trans.objectStore("store2").put({ abort: 6 }).onsuccess =
genCheck(601, { abort: 6, id: 601 }, "sixth" + test);
yield; yield;
trans.abort();
trans.onabort = grabEventAndContinueHandler;
event = yield
is(event.type, "abort", "transaction aborted");
is(event.target, trans, "correct transaction aborted");
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ abort: 1 }).onsuccess =
genCheck(c1++, { abort: 1 }, "re-first" + test);
trans.objectStore("store2").put({ abort: 2 }).onsuccess =
genCheck(c2, { abort: 2, id: c2 }, "re-second" + test);
c2++;
yield; yield;
// Test that delete doesn't decrease generator
test = " for test delete items"
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ delete: 1 }).onsuccess =
genCheck(c1++, { delete: 1 }, "first" + test);
trans.objectStore("store2").put({ delete: 2 }).onsuccess =
genCheck(c2, { delete: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").delete(c1 - 1).onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").delete(c2 - 1).onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans.objectStore("store1").add({ delete: 3 }).onsuccess =
genCheck(c1++, { delete: 3 }, "first" + test);
trans.objectStore("store2").put({ delete: 4 }).onsuccess =
genCheck(c2, { delete: 4, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").delete(c1 - 1).onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").delete(c2 - 1).onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ delete: 5 }).onsuccess =
genCheck(c1++, { delete: 5 }, "first" + test);
trans.objectStore("store2").put({ delete: 6 }).onsuccess =
genCheck(c2, { delete: 6, id: c2 }, "second" + test);
c2++;
yield; yield;
// Test that clears doesn't decrease generator
test = " for test clear stores";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ clear: 1 }).onsuccess =
genCheck(c1++, { clear: 1 }, "first" + test);
trans.objectStore("store2").put({ clear: 2 }).onsuccess =
genCheck(c2, { clear: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans.objectStore("store1").add({ clear: 3 }).onsuccess =
genCheck(c1++, { clear: 3 }, "third" + test);
trans.objectStore("store2").put({ clear: 4 }).onsuccess =
genCheck(c2, { clear: 4, id: c2 }, "forth" + test);
c2++;
yield; yield;
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ clear: 5 }).onsuccess =
genCheck(c1++, { clear: 5 }, "fifth" + test);
trans.objectStore("store2").put({ clear: 6 }).onsuccess =
genCheck(c2, { clear: 6, id: c2 }, "sixth" + test);
c2++;
yield; yield;
// Test that close/reopen doesn't decrease generator
test = " for test clear stores";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
db.close();
SpecialPowers.gc();
openRequest = indexedDB.open(dbname, 2);
openRequest.onerror = errorHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
event = yield;
db = event.target.result;
trans = event.target.transaction;
trans.objectStore("store1").add({ reopen: 1 }).onsuccess =
genCheck(c1++, { reopen: 1 }, "first" + test);
trans.objectStore("store2").put({ reopen: 2 }).onsuccess =
genCheck(c2, { reopen: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
openRequest.onsuccess = grabEventAndContinueHandler;
yield;
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_autoIncrement.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>

Просмотреть файл

@ -9,64 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
let request = indexedDB.open(name, 10);
request.onerror = errorHandler;
request.onsuccess = unexpectedSuccessHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let event = yield;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
let db = event.target.result;
db.createObjectStore("stuff");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.type, "success", "Expect a success event");
is(event.target, request, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
db.close();
let request = indexedDB.deleteDatabase(name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let openRequest = indexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
event = yield;
is(event.type, "success", "expect a success event");
is(event.target, request, "event has right target");
is(event.target.result, null, "event should have no result");
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.version, 1, "DB has proper version");
is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_interactions.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

Просмотреть файл

@ -9,175 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
const osName = "foo";
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
db.createObjectStore(osName, { autoIncrement: "true" });
yield;
let key1, key2;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.add({});
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
key1 = event.target.result;
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite").objectStore(osName).add({});
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
key2 = event.target.result;
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.put({}, key2);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.delete(key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.delete(key2);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
try {
request = db.transaction([osName]).objectStore(osName).add({});
ok(false, "Adding to a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding to a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).add({});
ok(false, "Adding to a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding to a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).put({});
ok(false, "Adding or modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding or modifying a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).put({});
ok(false, "Adding or modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding or modifying a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).put({}, key1);
ok(false, "Modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Modifying a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).put({}, key1);
ok(false, "Modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Modifying a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).delete(key1);
ok(false, "Removing from a readonly transaction should fail!");
}
catch (e) {
ok(true, "Removing from a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).delete(key2);
ok(false, "Removing from a readonly transaction should fail!");
}
catch (e) {
ok(true, "Removing from a readonly transaction failed");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_readonly_transactions.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

Просмотреть файл

@ -9,66 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
let request = indexedDB.open(window.location.pathname, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
for each (let autoIncrement in [false, true]) {
let objectStore =
db.createObjectStore(autoIncrement, { keyPath: "id",
autoIncrement: autoIncrement });
objectStore.createIndex("", "index", { unique: true });
for (let i = 0; i < 10; i++) {
objectStore.add({ id: i, index: i });
}
}
event = yield;
is(event.type, "success", "expect a success event");
for each (let autoIncrement in [false, true]) {
objectStore = db.transaction(autoIncrement, IDBTransaction.READ_WRITE)
.objectStore(autoIncrement);
request = objectStore.put({ id: 5, index: 6 });
request.onsuccess = unexpectedSuccessHandler;
request.addEventListener("error", new ExpectError("ConstraintError", true));
event = yield;
event.preventDefault();
let keyRange = IDBKeyRange.only(5);
objectStore.index("").openCursor(keyRange).onsuccess = function(event) {
let cursor = event.target.result;
ok(cursor, "Must have a cursor here");
is(cursor.value.index, 5, "Still have the right index value");
cursor.value.index = 6;
request = cursor.update(cursor.value);
request.onsuccess = unexpectedSuccessHandler;
request.addEventListener("error", new ExpectError("ConstraintError", true));
};
yield;
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_unique_index_update.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

Просмотреть файл

@ -9,98 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
// Needs to be enough to saturate the thread pool.
const SYNC_REQUEST_COUNT = 25;
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
db.onerror = errorHandler;
is(event.target.transaction.mode, "versionchange", "Correct mode");
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
request = objectStore.add({});
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let key = event.target.result;
ok(key, "Got a key");
yield;
let continueReading = true;
let readerCount = 0;
let writerCount = 0;
let callbackCount = 0;
// Generate a bunch of reads right away without returning to the event
// loop.
info("Generating " + SYNC_REQUEST_COUNT + " readonly requests");
for (let i = 0; i < SYNC_REQUEST_COUNT; i++) {
readerCount++;
let request = db.transaction("foo").objectStore("foo").get(key);
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readonly", "Correct mode");
callbackCount++;
};
}
while (continueReading) {
readerCount++;
info("Generating additional readonly request (" + readerCount + ")");
let request = db.transaction("foo").objectStore("foo").get(key);
request.onsuccess = function(event) {
callbackCount++;
info("Received readonly request callback (" + callbackCount + ")");
is(event.target.transaction.mode, "readonly", "Correct mode");
if (callbackCount == SYNC_REQUEST_COUNT) {
writerCount++;
info("Generating 1 readwrite request with " + readerCount +
" previous readonly requests");
let request = db.transaction("foo", "readwrite")
.objectStore("foo")
.add({}, readerCount);
request.onsuccess = function(event) {
callbackCount++;
info("Received readwrite request callback (" + callbackCount + ")");
is(event.target.transaction.mode, "readwrite", "Correct mode");
is(event.target.result, callbackCount,
"write callback came before later reads");
}
}
else if (callbackCount == SYNC_REQUEST_COUNT + 5) {
continueReading = false;
}
};
setTimeout(function() { testGenerator.next(); }, writerCount ? 1000 : 100);
yield;
}
while (callbackCount < (readerCount + writerCount)) {
executeSoon(function() { testGenerator.next(); });
yield;
}
is(callbackCount, readerCount + writerCount, "All requests accounted for");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_writer_starvation.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

Просмотреть файл

@ -14,6 +14,7 @@ MOCHITEST_FILES = \
test_add_put.js \
test_add_twice_failure.js \
test_advance.js \
test_autoIncrement.js \
test_autoIncrement_indexes.js \
test_clear.js \
test_complex_keyPaths.js \
@ -25,6 +26,7 @@ MOCHITEST_FILES = \
test_cursor_update_updates_indexes.js \
test_cursors.js \
test_deleteDatabase.js \
test_deleteDatabase_interactions.js \
test_event_source.js \
test_getAll.js \
test_global_data.js \
@ -54,6 +56,7 @@ MOCHITEST_FILES = \
test_overlapping_transactions.js \
test_put_get_values.js \
test_put_get_values_autoIncrement.js \
test_readonly_transactions.js \
test_remove_index.js \
test_remove_objectStore.js \
test_request_readyState.js \
@ -68,6 +71,8 @@ MOCHITEST_FILES = \
test_transaction_lifetimes.js \
test_transaction_lifetimes_nested.js \
test_transaction_ordering.js \
test_unique_index_update.js \
test_writer_starvation.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -38,8 +38,8 @@ function todo(condition, name, diag) {
dump("TODO: ", diag);
}
function info(msg) {
do_print(msg);
function info(name, message) {
do_print(name);
}
function run_test() {
@ -193,6 +193,19 @@ function gc()
Components.utils.forceCC();
}
function setTimeout(fun, timeout) {
let timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
var event = {
notify: function (timer) {
fun();
}
};
timer.initWithCallback(event, timeout,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
return timer;
}
var SpecialPowers = {
isMainProcess: function() {
return Components.classes["@mozilla.org/xre/app-info;1"]

Просмотреть файл

@ -0,0 +1,398 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
if (!this.window) {
this.runTest = function() {
todo(false, "Test disabled in xpcshell test suite for now");
finishTest();
}
}
var testGenerator = testSteps();
function genCheck(key, value, test, options) {
return function(event) {
is(JSON.stringify(event.target.result), JSON.stringify(key),
"correct returned key in " + test);
if (options && options.store) {
is(event.target.source, options.store, "correct store in " + test);
}
if (options && options.trans) {
is(event.target.transaction, options.trans, "correct transaction in " + test);
}
event.target.source.get(key).onsuccess = function(event) {
is(JSON.stringify(event.target.result), JSON.stringify(value),
"correct stored value in " + test);
continueToNextStepSync();
}
}
}
function testSteps()
{
const dbname = this.window ? window.location.pathname : "Splendid Test";
const RW = "readwrite";
let c1 = 1;
let c2 = 1;
let openRequest = indexedDB.open(dbname, 1);
openRequest.onerror = errorHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
let event = yield;
let db = event.target.result;
let trans = event.target.transaction;
// Create test stores
let store1 = db.createObjectStore("store1", { autoIncrement: true });
let store2 = db.createObjectStore("store2", { autoIncrement: true, keyPath: "id" });
let store3 = db.createObjectStore("store3", { autoIncrement: false });
is(store1.autoIncrement, true, "store1 .autoIncrement");
is(store2.autoIncrement, true, "store2 .autoIncrement");
is(store3.autoIncrement, false, "store3 .autoIncrement");
store1.createIndex("unique1", "unique", { unique: true });
store2.createIndex("unique1", "unique", { unique: true });
// Test simple inserts
let test = " for test simple insert"
store1.add({ foo: "value1" }).onsuccess =
genCheck(c1++, { foo: "value1" }, "first" + test);
store1.add({ foo: "value2" }).onsuccess =
genCheck(c1++, { foo: "value2" }, "second" + test);
yield;
yield;
store2.put({ bar: "value1" }).onsuccess =
genCheck(c2, { bar: "value1", id: c2 }, "first in store2" + test,
{ store: store2 });
c2++;
store1.put({ foo: "value3" }).onsuccess =
genCheck(c1++, { foo: "value3" }, "third" + test,
{ store: store1 });
yield;
yield;
store2.get(IDBKeyRange.lowerBound(c2)).onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, undefined, "no such value" + test);
// Close version_change transaction
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target, openRequest, "succeeded to open" + test);
is(event.type, "success", "succeeded to open" + test);
// Test inserting explicit keys
test = " for test explicit keys";
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 1 }, 100).onsuccess =
genCheck(100, { explicit: 1 }, "first" + test);
c1 = 101;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 2 }).onsuccess =
genCheck(c1++, { explicit: 2 }, "second" + test);
yield; yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 3 }, 200).onsuccess =
genCheck(200, { explicit: 3 }, "third" + test);
c1 = 201;
trans.objectStore("store1").add({ explicit: 4 }).onsuccess =
genCheck(c1++, { explicit: 4 }, "fourth" + test);
yield; yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 5 }, 150).onsuccess =
genCheck(150, { explicit: 5 }, "fifth" + test);
yield;
trans.objectStore("store1").add({ explicit: 6 }).onsuccess =
genCheck(c1++, { explicit: 6 }, "sixth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 7 }, "key").onsuccess =
genCheck("key", { explicit: 7 }, "seventh" + test);
yield;
trans.objectStore("store1").add({ explicit: 8 }).onsuccess =
genCheck(c1++, { explicit: 8 }, "eighth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 7 }, [100000]).onsuccess =
genCheck([100000], { explicit: 7 }, "seventh" + test);
yield;
trans.objectStore("store1").add({ explicit: 8 }).onsuccess =
genCheck(c1++, { explicit: 8 }, "eighth" + test);
yield;
trans = db.transaction("store1", RW);
trans.objectStore("store1").add({ explicit: 9 }, -100000).onsuccess =
genCheck(-100000, { explicit: 9 }, "ninth" + test);
yield;
trans.objectStore("store1").add({ explicit: 10 }).onsuccess =
genCheck(c1++, { explicit: 10 }, "tenth" + test);
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 1, id: 300 }).onsuccess =
genCheck(300, { explicit2: 1, id: 300 }, "first store2" + test);
c2 = 301;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 2 }).onsuccess =
genCheck(c2, { explicit2: 2, id: c2 }, "second store2" + test);
c2++;
yield; yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit2: 3, id: 400 }).onsuccess =
genCheck(400, { explicit2: 3, id: 400 }, "third store2" + test);
c2 = 401;
trans.objectStore("store2").add({ explicit2: 4 }).onsuccess =
genCheck(c2, { explicit2: 4, id: c2 }, "fourth store2" + test);
c2++;
yield; yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 5, id: 150 }).onsuccess =
genCheck(150, { explicit: 5, id: 150 }, "fifth store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 6 }).onsuccess =
genCheck(c2, { explicit: 6, id: c2 }, "sixth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 7, id: "key" }).onsuccess =
genCheck("key", { explicit: 7, id: "key" }, "seventh store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 8 }).onsuccess =
genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 7, id: [100000] }).onsuccess =
genCheck([100000], { explicit: 7, id: [100000] }, "seventh store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 8 }).onsuccess =
genCheck(c2, { explicit: 8, id: c2 }, "eighth store2" + test);
c2++;
yield;
trans = db.transaction("store2", RW);
trans.objectStore("store2").add({ explicit: 9, id: -100000 }).onsuccess =
genCheck(-100000, { explicit: 9, id: -100000 }, "ninth store2" + test);
yield;
trans.objectStore("store2").add({ explicit: 10 }).onsuccess =
genCheck(c2, { explicit: 10, id: c2 }, "tenth store2" + test);
c2++;
yield;
// Test separate transactions doesn't generate overlapping numbers
test = " for test non-overlapping counts";
trans = db.transaction("store1", RW);
trans2 = db.transaction("store1", RW);
trans2.objectStore("store1").put({ over: 2 }).onsuccess =
genCheck(c1 + 1, { over: 2 }, "first" + test,
{ trans: trans2 });
trans.objectStore("store1").put({ over: 1 }).onsuccess =
genCheck(c1, { over: 1 }, "second" + test,
{ trans: trans });
c1 += 2;
yield; yield;
trans = db.transaction("store2", RW);
trans2 = db.transaction("store2", RW);
trans2.objectStore("store2").put({ over: 2 }).onsuccess =
genCheck(c2 + 1, { over: 2, id: c2 + 1 }, "third" + test,
{ trans: trans2 });
trans.objectStore("store2").put({ over: 1 }).onsuccess =
genCheck(c2, { over: 1, id: c2 }, "fourth" + test,
{ trans: trans });
c2 += 2;
yield; yield;
// Test that error inserts doesn't increase generator
test = " for test error inserts";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ unique: 1 }, -1);
trans.objectStore("store2").add({ unique: 1, id: "unique" });
trans.objectStore("store1").add({ error: 1, unique: 1 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store1").add({ error: 2 }).onsuccess =
genCheck(c1++, { error: 2 }, "first" + test);
yield; yield;
trans.objectStore("store2").add({ error: 3, unique: 1 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store2").add({ error: 4 }).onsuccess =
genCheck(c2, { error: 4, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").add({ error: 5, unique: 1 }, 100000).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store1").add({ error: 6 }).onsuccess =
genCheck(c1++, { error: 6 }, "third" + test);
yield; yield;
trans.objectStore("store2").add({ error: 7, unique: 1, id: 100000 }).
addEventListener("error", new ExpectError("ConstraintError", true));
trans.objectStore("store2").add({ error: 8 }).onsuccess =
genCheck(c2, { error: 8, id: c2 }, "fourth" + test);
c2++;
yield; yield;
// Test that aborts doesn't increase generator
test = " for test aborted transaction";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ abort: 1 }).onsuccess =
genCheck(c1, { abort: 1 }, "first" + test);
trans.objectStore("store2").put({ abort: 2 }).onsuccess =
genCheck(c2, { abort: 2, id: c2 }, "second" + test);
yield; yield;
trans.objectStore("store1").add({ abort: 3 }, 500).onsuccess =
genCheck(500, { abort: 3 }, "third" + test);
trans.objectStore("store2").put({ abort: 4, id: 600 }).onsuccess =
genCheck(600, { abort: 4, id: 600 }, "fourth" + test);
yield; yield;
trans.objectStore("store1").add({ abort: 5 }).onsuccess =
genCheck(501, { abort: 5 }, "fifth" + test);
trans.objectStore("store2").put({ abort: 6 }).onsuccess =
genCheck(601, { abort: 6, id: 601 }, "sixth" + test);
yield; yield;
trans.abort();
trans.onabort = grabEventAndContinueHandler;
event = yield
is(event.type, "abort", "transaction aborted");
is(event.target, trans, "correct transaction aborted");
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ abort: 1 }).onsuccess =
genCheck(c1++, { abort: 1 }, "re-first" + test);
trans.objectStore("store2").put({ abort: 2 }).onsuccess =
genCheck(c2, { abort: 2, id: c2 }, "re-second" + test);
c2++;
yield; yield;
// Test that delete doesn't decrease generator
test = " for test delete items"
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ delete: 1 }).onsuccess =
genCheck(c1++, { delete: 1 }, "first" + test);
trans.objectStore("store2").put({ delete: 2 }).onsuccess =
genCheck(c2, { delete: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").delete(c1 - 1).onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").delete(c2 - 1).onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans.objectStore("store1").add({ delete: 3 }).onsuccess =
genCheck(c1++, { delete: 3 }, "first" + test);
trans.objectStore("store2").put({ delete: 4 }).onsuccess =
genCheck(c2, { delete: 4, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").delete(c1 - 1).onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").delete(c2 - 1).onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ delete: 5 }).onsuccess =
genCheck(c1++, { delete: 5 }, "first" + test);
trans.objectStore("store2").put({ delete: 6 }).onsuccess =
genCheck(c2, { delete: 6, id: c2 }, "second" + test);
c2++;
yield; yield;
// Test that clears doesn't decrease generator
test = " for test clear stores";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ clear: 1 }).onsuccess =
genCheck(c1++, { clear: 1 }, "first" + test);
trans.objectStore("store2").put({ clear: 2 }).onsuccess =
genCheck(c2, { clear: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans.objectStore("store1").add({ clear: 3 }).onsuccess =
genCheck(c1++, { clear: 3 }, "third" + test);
trans.objectStore("store2").put({ clear: 4 }).onsuccess =
genCheck(c2, { clear: 4, id: c2 }, "forth" + test);
c2++;
yield; yield;
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").add({ clear: 5 }).onsuccess =
genCheck(c1++, { clear: 5 }, "fifth" + test);
trans.objectStore("store2").put({ clear: 6 }).onsuccess =
genCheck(c2, { clear: 6, id: c2 }, "sixth" + test);
c2++;
yield; yield;
// Test that close/reopen doesn't decrease generator
test = " for test clear stores";
trans = db.transaction(["store1", "store2"], RW);
trans.objectStore("store1").clear().onsuccess =
grabEventAndContinueHandler;
trans.objectStore("store2").clear().onsuccess =
grabEventAndContinueHandler;
yield; yield;
db.close();
SpecialPowers.gc();
openRequest = indexedDB.open(dbname, 2);
openRequest.onerror = errorHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
event = yield;
db = event.target.result;
trans = event.target.transaction;
trans.objectStore("store1").add({ reopen: 1 }).onsuccess =
genCheck(c1++, { reopen: 1 }, "first" + test);
trans.objectStore("store2").put({ reopen: 2 }).onsuccess =
genCheck(c2, { reopen: 2, id: c2 }, "second" + test);
c2++;
yield; yield;
openRequest.onsuccess = grabEventAndContinueHandler;
yield;
finishTest();
yield;
}

Просмотреть файл

@ -0,0 +1,62 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
let request = indexedDB.open(name, 10);
request.onerror = errorHandler;
request.onsuccess = unexpectedSuccessHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let event = yield;
is(event.type, "upgradeneeded", "Expect an upgradeneeded event");
ok(event instanceof IDBVersionChangeEvent, "Expect a versionchange event");
let db = event.target.result;
db.createObjectStore("stuff");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.type, "success", "Expect a success event");
is(event.target, request, "Event has right target");
ok(event.target.result instanceof IDBDatabase, "Result should be a database");
is(db.objectStoreNames.length, 1, "Expect an objectStore here");
db.close();
let request = indexedDB.deleteDatabase(name);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
ok(request instanceof IDBOpenDBRequest, "Expect an IDBOpenDBRequest");
let openRequest = indexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
event = yield;
is(event.type, "success", "expect a success event");
is(event.target, request, "event has right target");
is(event.target.result, null, "event should have no result");
openRequest.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.version, 1, "DB has proper version");
is(event.target.result.objectStoreNames.length, 0, "DB should have no object stores");
finishTest();
yield;
}

Просмотреть файл

@ -0,0 +1,181 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
if (!this.window) {
this.runTest = function() {
todo(false, "Test disabled in xpcshell test suite for now");
finishTest();
}
}
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
const osName = "foo";
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
db.createObjectStore(osName, { autoIncrement: "true" });
yield;
let key1, key2;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.add({});
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
key1 = event.target.result;
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite").objectStore(osName).add({});
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
key2 = event.target.result;
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.put({}, key2);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.put({}, key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction([osName], "readwrite")
.objectStore(osName)
.delete(key1);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
request = db.transaction(osName, "readwrite")
.objectStore(osName)
.delete(key2);
request.onerror = errorHandler;
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readwrite", "Correct mode");
testGenerator.next();
}
yield;
try {
request = db.transaction([osName]).objectStore(osName).add({});
ok(false, "Adding to a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding to a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).add({});
ok(false, "Adding to a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding to a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).put({});
ok(false, "Adding or modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding or modifying a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).put({});
ok(false, "Adding or modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Adding or modifying a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).put({}, key1);
ok(false, "Modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Modifying a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).put({}, key1);
ok(false, "Modifying a readonly transaction should fail!");
}
catch (e) {
ok(true, "Modifying a readonly transaction failed");
}
try {
request = db.transaction([osName]).objectStore(osName).delete(key1);
ok(false, "Removing from a readonly transaction should fail!");
}
catch (e) {
ok(true, "Removing from a readonly transaction failed");
}
try {
request = db.transaction(osName).objectStore(osName).delete(key2);
ok(false, "Removing from a readonly transaction should fail!");
}
catch (e) {
ok(true, "Removing from a readonly transaction failed");
}
finishTest();
yield;
}

Просмотреть файл

@ -0,0 +1,64 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
for each (let autoIncrement in [false, true]) {
let objectStore =
db.createObjectStore(autoIncrement, { keyPath: "id",
autoIncrement: autoIncrement });
objectStore.createIndex("", "index", { unique: true });
for (let i = 0; i < 10; i++) {
objectStore.add({ id: i, index: i });
}
}
event = yield;
is(event.type, "success", "expect a success event");
for each (let autoIncrement in [false, true]) {
objectStore = db.transaction(autoIncrement, "readwrite")
.objectStore(autoIncrement);
request = objectStore.put({ id: 5, index: 6 });
request.onsuccess = unexpectedSuccessHandler;
request.addEventListener("error", new ExpectError("ConstraintError", true));
event = yield;
event.preventDefault();
let keyRange = IDBKeyRange.only(5);
objectStore.index("").openCursor(keyRange).onsuccess = function(event) {
let cursor = event.target.result;
ok(cursor, "Must have a cursor here");
is(cursor.value.index, 5, "Still have the right index value");
cursor.value.index = 6;
request = cursor.update(cursor.value);
request.onsuccess = unexpectedSuccessHandler;
request.addEventListener("error", new ExpectError("ConstraintError", true));
};
yield;
}
finishTest();
yield;
}

Просмотреть файл

@ -0,0 +1,104 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
if (!this.window) {
this.runTest = function() {
todo(false, "Test disabled in xpcshell test suite for now");
finishTest();
}
}
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
// Needs to be enough to saturate the thread pool.
const SYNC_REQUEST_COUNT = 25;
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.target.result;
db.onerror = errorHandler;
is(event.target.transaction.mode, "versionchange", "Correct mode");
let objectStore = db.createObjectStore("foo", { autoIncrement: true });
request = objectStore.add({});
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let key = event.target.result;
ok(key, "Got a key");
yield;
let continueReading = true;
let readerCount = 0;
let writerCount = 0;
let callbackCount = 0;
// Generate a bunch of reads right away without returning to the event
// loop.
info("Generating " + SYNC_REQUEST_COUNT + " readonly requests");
for (let i = 0; i < SYNC_REQUEST_COUNT; i++) {
readerCount++;
let request = db.transaction("foo").objectStore("foo").get(key);
request.onsuccess = function(event) {
is(event.target.transaction.mode, "readonly", "Correct mode");
callbackCount++;
};
}
while (continueReading) {
readerCount++;
info("Generating additional readonly request (" + readerCount + ")");
let request = db.transaction("foo").objectStore("foo").get(key);
request.onsuccess = function(event) {
callbackCount++;
info("Received readonly request callback (" + callbackCount + ")");
is(event.target.transaction.mode, "readonly", "Correct mode");
if (callbackCount == SYNC_REQUEST_COUNT) {
writerCount++;
info("Generating 1 readwrite request with " + readerCount +
" previous readonly requests");
let request = db.transaction("foo", "readwrite")
.objectStore("foo")
.add({}, readerCount);
request.onsuccess = function(event) {
callbackCount++;
info("Received readwrite request callback (" + callbackCount + ")");
is(event.target.transaction.mode, "readwrite", "Correct mode");
is(event.target.result, callbackCount,
"write callback came before later reads");
}
}
else if (callbackCount == SYNC_REQUEST_COUNT + 5) {
continueReading = false;
}
};
setTimeout(function() { testGenerator.next(); }, writerCount ? 1000 : 100);
yield;
}
while (callbackCount < (readerCount + writerCount)) {
executeSoon(function() { testGenerator.next(); });
yield;
}
is(callbackCount, readerCount + writerCount, "All requests accounted for");
finishTest();
yield;
}

Просмотреть файл

@ -7,6 +7,7 @@ tail =
[test_add_put.js]
[test_add_twice_failure.js]
[test_advance.js]
[test_autoIncrement.js]
[test_autoIncrement_indexes.js]
[test_clear.js]
[test_complex_keyPaths.js]
@ -18,6 +19,7 @@ tail =
[test_cursor_update_updates_indexes.js]
[test_cursors.js]
[test_deleteDatabase.js]
[test_deleteDatabase_interactions.js]
[test_event_source.js]
[test_getAll.js]
[test_global_data.js]
@ -47,6 +49,7 @@ tail =
[test_overlapping_transactions.js]
[test_put_get_values.js]
[test_put_get_values_autoIncrement.js]
[test_readonly_transactions.js]
[test_remove_index.js]
[test_remove_objectStore.js]
[test_request_readyState.js]
@ -61,5 +64,7 @@ tail =
[test_transaction_lifetimes.js]
[test_transaction_lifetimes_nested.js]
[test_transaction_ordering.js]
[test_unique_index_update.js]
[test_writer_starvation.js]
# When adding files here please also update ipc/unit/xpcshell.ini!

Просмотреть файл

@ -40,7 +40,6 @@ MOCHITEST_FILES = \
test_peerConnection_bug827843.html \
test_peerConnection_bug834153.html \
test_peerConnection_bug835370.html \
test_peerConnection_bug840344.html \
head.js \
mediaStreamPlayback.js \
pc.js \

Просмотреть файл

@ -1,119 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="head.js"></script>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "840344",
title: "Prevent multiple creations of local SDP"
});
// SDP to stand in for an offer coming from a (theoretical) remote endpoint
var offer = {
sdp: "v=0\r\n"+
"o=Mozilla-SIPUA 23597 0 IN IP4 0.0.0.0\r\n"+
"s=SIP Call\r\n"+
"t=0 0\r\n"+
"a=ice-ufrag:f5fda439\r\n"+
"a=ice-pwd:d0df8e2904bdbd29587966e797655970\r\n"+
"a=fingerprint:sha-256 DF:69:78:20:8D:2E:08:CE:49:82:A3:11:79:1D:BF:"+
"B5:49:49:2D:32:82:2F:0D:88:84:A7:C6:63:23:63:A9:0F\r\n"+
"m=audio 52757 RTP/SAVPF 109 0 8 101\r\n"+
"c=IN IP4 192.168.129.33\r\n"+
"a=rtpmap:109 opus/48000/2\r\n"+
"a=ptime:20\r\n"+
"a=rtpmap:0 PCMU/8000\r\n"+
"a=rtpmap:8 PCMA/8000\r\n"+
"a=rtpmap:101 telephone-event/8000\r\n"+
"a=fmtp:101 0-15\r\n"+
"a=sendrecv\r\n"+
"a=candidate:0 1 UDP 2113601791 192.168.129.33 52757 typ host\r\n"+
"a=candidate:0 2 UDP 2113601790 192.168.129.33 59738 typ host\r\n"+
"m=video 63901 RTP/SAVPF 120\r\n"+
"c=IN IP4 192.168.129.33\r\n"+
"a=rtpmap:120 VP8/90000\r\n"+
"a=sendrecv\r\n"+
"a=candidate:0 1 UDP 2113601791 192.168.129.33 63901 typ host\r\n"+
"a=candidate:0 2 UDP 2113601790 192.168.129.33 54165 typ host\r\n"+
"m=application 65080 SCTP/DTLS 5000 \r\n"+
"c=IN IP4 192.168.129.33\r\n"+
"a=fmtp:5000 protocol=webrtc-datachannel;streams=16\r\n"+
"a=sendrecv\r\n"+
"a=candidate:0 1 UDP 2113601791 192.168.129.33 65080 typ host\r\n"+
"a=candidate:0 2 UDP 2113601790 192.168.129.33 62658 typ host\r\n",
type: "offer"
};
var steps = [
[
"SET_REMOTE_DESCRIPTION",
function (test) {
test.pcLocal.setRemoteDescription(new mozRTCSessionDescription(offer),
function () {
test.next();
});
}
],
[
"CHECK_MULTIPLE_ANSWERS",
function (test) {
var answerCount = 0;
var setLocalCount = 0;
info("Create answer #1");
test.pcLocal.createAnswer(answerSuccess);
info("Create answer #2");
test.pcLocal.createAnswer(answerSuccess);
// Fourth: Count the answers and push them into the local description
function answerSuccess(answer) {
answerCount++;
ok (answerCount < 3, "Answer count is less than three.")
info("Got answer #" + answerCount);
is(answer.type, 'answer', "Answer is of type 'answer'");
ok(answer.sdp.length > 10, "Answer has length " + answer.sdp.length);
info("Set local description for answer #" + answerCount);
test.pcLocal.setLocalDescription(answer, setLocalSuccess);
}
// Fifth: Once we have two successful rounds through here, we're done.
function setLocalSuccess() {
setLocalCount++;
info("Set local description #" + setLocalCount);
// Then shalt thou count to two, no more, no less. Two shall be the
// number thou shalt count, and the number of the counting shall be
// two. Three shalt thou not count, neither count thou one, excepting
// that thou then proceed to two. Four is right out. Once the number two,
// being the second number, be reached, then thou shalt declare success.
ok (setLocalCount < 3, "Set local count is less than three.")
if (setLocalCount === 2) {
is (answerCount, 2, "Answer count is 2.")
test.next();
}
}
}
]
];
var test;
runTest(function () {
test = new PeerConnectionTest();
test.setMediaConstraints([{audio: true, video: true}], [ ]);
test.chain.replaceAfter("PC_LOCAL_GUM", steps);
test.run();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -12,6 +12,7 @@
* liability, trademark and document use rules apply.
*/
[Constructor]
interface Range {
[Throws]
readonly attribute Node startContainer;

Просмотреть файл

@ -10,6 +10,7 @@
* liability, trademark and document use rules apply.
*/
[Constructor(optional DOMString data = "")]
interface Text : CharacterData {
[Throws]
Text splitText(unsigned long offset);

Просмотреть файл

@ -4977,13 +4977,12 @@ nsEditor::FinalizeSelection()
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
NS_ENSURE_TRUE_VOID(presShell);
nsRefPtr<nsCaret> caret = presShell->GetCaret();
if (caret) {
caret->SetIgnoreUserModify(true);
}
selCon->SetCaretEnabled(false);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE_VOID(fm);
fm->UpdateCaretForCaretBrowsingMode();
if (!HasIndependentSelection()) {
// If this editor doesn't have an independent selection, i.e., it must
// mean that it is an HTML editor, the selection controller is shared with

Просмотреть файл

@ -355,7 +355,8 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
"Adreno (TM) 205",
"Adreno (TM) 320",
"PowerVR SGX 530",
"PowerVR SGX 540"
"PowerVR SGX 540",
"NVIDIA Tegra"
};
mRenderer = RendererOther;

Просмотреть файл

@ -301,6 +301,7 @@ public:
RendererAdrenoTM320,
RendererSGX530,
RendererSGX540,
RendererTegra,
RendererOther
};

Просмотреть файл

@ -109,7 +109,6 @@ ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
// don't signal a new transaction to ShadowLayerForwarder. Carry on adding
// to the previous transaction.
ScreenOrientation orientation;
nsIntRect clientBounds;
if (TabChild* window = mWidget->GetOwningTabChild()) {
orientation = window->GetOrientation();
} else {
@ -117,7 +116,9 @@ ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
hal::GetCurrentScreenConfiguration(&currentConfig);
orientation = currentConfig.orientation();
}
nsIntRect clientBounds;
mWidget->GetClientBounds(clientBounds);
clientBounds.x = clientBounds.y = 0;
ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation);
// If we're drawing on behalf of a context with async pan/zoom

Просмотреть файл

@ -97,6 +97,7 @@ TextureClientShmem::SetDescriptor(const SurfaceDescriptor& aDescriptor)
NS_ASSERTION(mDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc ||
mDescriptor.type() == SurfaceDescriptor::TShmem ||
mDescriptor.type() == SurfaceDescriptor::TMemoryImage ||
mDescriptor.type() == SurfaceDescriptor::TRGBImage,
"Invalid surface descriptor");
}

Просмотреть файл

@ -450,7 +450,8 @@ ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation)
{
mUpdateList.AppendElement(new TextureUpdateRequest(aTextureId,
mUpdateList.AppendElement(new TextureUpdateRequest(mDeAllocator,
aTextureId,
aSurface,
aUpdated,
aBufferRect,
@ -642,9 +643,6 @@ ContentHostIncremental::TextureUpdateRequest::Execute(ContentHostIncremental* aH
} else {
aHost->mTextureHostOnWhite->Update(mDescriptor, &mUpdated, &offset);
}
//TODO: Recycle these?
aHost->mDeAllocator->DestroySharedSurface(&mDescriptor);
}
#ifdef MOZ_LAYERS_HAVE_LOG

Просмотреть файл

@ -308,18 +308,26 @@ private:
class TextureUpdateRequest : public Request
{
public:
TextureUpdateRequest(TextureIdentifier aTextureId,
TextureUpdateRequest(ISurfaceAllocator* aDeAllocator,
TextureIdentifier aTextureId,
SurfaceDescriptor& aDescriptor,
const nsIntRegion& aUpdated,
const nsIntRect& aBufferRect,
const nsIntPoint& aBufferRotation)
: mTextureId(aTextureId)
: mDeAllocator(aDeAllocator)
, mTextureId(aTextureId)
, mDescriptor(aDescriptor)
, mUpdated(aUpdated)
, mBufferRect(aBufferRect)
, mBufferRotation(aBufferRotation)
{}
~TextureUpdateRequest()
{
//TODO: Recycle these?
mDeAllocator->DestroySharedSurface(&mDescriptor);
}
virtual void Execute(ContentHostIncremental *aHost);
private:
@ -332,6 +340,7 @@ private:
nsIntRect GetQuadrantRectangle(XSide aXSide, YSide aYSide) const;
ISurfaceAllocator* mDeAllocator;
TextureIdentifier mTextureId;
SurfaceDescriptor mDescriptor;
nsIntRegion mUpdated;

Просмотреть файл

@ -13,6 +13,7 @@
#include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/layers/SharedPlanarYCbCrImage.h"
#include "mozilla/layers/SharedRGBImage.h"
#include "nsXULAppAPI.h"
#ifdef DEBUG
#include "prenv.h"
@ -85,6 +86,16 @@ ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfxIntSize& aSize,
return true;
}
if (XRE_GetProcessType() == GeckoProcessType_Default) {
gfxImageFormat format =
gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
int32_t stride = gfxASurface::FormatStrideForWidth(format, aSize.width);
uint8_t *data = new uint8_t[stride * aSize.height];
*aBuffer = MemoryImage((uintptr_t)data, aSize, stride, format);
return true;
}
nsRefPtr<gfxSharedImageSurface> buffer;
if (!AllocSharedImageSurface(aSize, aContent,
getter_AddRefs(buffer))) {
@ -122,6 +133,9 @@ ISurfaceAllocator::DestroySharedSurface(SurfaceDescriptor* aSurface)
break;
case SurfaceDescriptor::TSurfaceDescriptorD3D10:
break;
case SurfaceDescriptor::TMemoryImage:
delete [] (unsigned char *)aSurface->get_MemoryImage().data();
break;
case SurfaceDescriptor::Tnull_t:
case SurfaceDescriptor::T__None:
break;

Просмотреть файл

@ -83,6 +83,13 @@ struct RGBImage {
uint64_t owner;
};
struct MemoryImage {
uintptr_t data;
gfxIntSize size;
uint32_t stride;
uint32_t format;
};
union SurfaceDescriptor {
Shmem;
SurfaceDescriptorD3D10;
@ -92,6 +99,7 @@ union SurfaceDescriptor {
RGBImage;
SharedTextureDescriptor;
SurfaceStreamDescriptor;
MemoryImage;
null_t;
};

Просмотреть файл

@ -32,10 +32,22 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfxIntSize& aSize,
ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode aMode,
const SurfaceDescriptor& aSurface)
{
if (SurfaceDescriptor::TShmem != aSurface.type()) {
return nullptr;
}
if (aSurface.type() == SurfaceDescriptor::TShmem) {
return gfxSharedQuartzSurface::Open(aSurface.get_Shmem());
} else if (aSurface.type() == SurfaceDescriptor::TMemoryImage) {
const MemoryImage& image = aSurface.get_MemoryImage();
gfxASurface::gfxImageFormat format
= static_cast<gfxASurface::gfxImageFormat>(image.format());
nsRefPtr<gfxASurface> surf =
new gfxQuartzSurface((unsigned char*)image.data(),
image.size(),
image.stride(),
format);
return surf.forget();
}
return nullptr;
}
/*static*/ bool

Просмотреть файл

@ -358,7 +358,7 @@ ShadowLayerForwarder::UpdateTextureIncremental(CompositableClient* aCompositable
{
MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPDLActor());
mTxn->AddPaint(OpPaintTextureIncremental(nullptr, aCompositable->GetIPDLActor(),
mTxn->AddNoSwapPaint(OpPaintTextureIncremental(nullptr, aCompositable->GetIPDLActor(),
aTextureId,
aDescriptor,
aUpdatedRegion,
@ -530,6 +530,16 @@ ShadowLayerForwarder::OpenDescriptor(OpenMode aMode,
rgbFormat);
return surf.forget();
}
case SurfaceDescriptor::TMemoryImage: {
const MemoryImage& image = aSurface.get_MemoryImage();
gfxASurface::gfxImageFormat format
= static_cast<gfxASurface::gfxImageFormat>(image.format());
surf = new gfxImageSurface((unsigned char *)image.data(),
image.size(),
image.stride(),
format);
return surf.forget();
}
default:
NS_ERROR("unexpected SurfaceDescriptor type!");
return nullptr;
@ -730,7 +740,7 @@ ShadowLayerForwarder::CreatedIncrementalBuffer(CompositableClient* aCompositable
const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect)
{
mTxn->AddPaint(OpCreatedIncrementalTexture(nullptr, aCompositable->GetIPDLActor(),
mTxn->AddNoSwapPaint(OpCreatedIncrementalTexture(nullptr, aCompositable->GetIPDLActor(),
aTextureInfo, aBufferRect));
}

Просмотреть файл

@ -777,7 +777,7 @@ CompositorOGL::BeginFrame(const Rect *aClipRectIn, const gfxMatrix& aTransform,
// sent atomically with rotation changes
nsIntRect intRect;
mWidget->GetClientBounds(intRect);
rect = gfxRect(intRect.x, intRect.y, intRect.width, intRect.height);
rect = gfxRect(0, 0, intRect.width, intRect.height);
}
}

Просмотреть файл

@ -786,6 +786,7 @@ LayerManagerOGL::Render()
// thread, and undoes all the care we take with layers txns being
// sent atomically with rotation changes
mWidget->GetClientBounds(rect);
rect.x = rect.y = 0;
}
}
WorldTransformRect(rect);

Просмотреть файл

@ -125,6 +125,29 @@ gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
}
}
gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
const gfxIntSize& aSize,
long stride,
gfxImageFormat format,
bool aForPrinting)
: mCGContext(nullptr), mSize(aSize.width, aSize.height), mForPrinting(aForPrinting)
{
if (!CheckSurfaceSize(aSize))
MakeInvalid();
cairo_surface_t *surf = cairo_quartz_surface_create_for_data
(data, (cairo_format_t) format, aSize.width, aSize.height, stride);
mCGContext = cairo_quartz_surface_get_cg_context (surf);
CGContextRetain(mCGContext);
Init(surf);
if (mSurfaceValid) {
RecordMemoryUsed(mSize.height * stride + sizeof(gfxQuartzSurface));
}
}
already_AddRefed<gfxASurface>
gfxQuartzSurface::CreateSimilarSurface(gfxContentType aType,
const gfxIntSize& aSize)

Просмотреть файл

@ -20,6 +20,7 @@ public:
gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
gfxQuartzSurface(cairo_surface_t *csurf, bool aForPrinting = false);
gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
gfxQuartzSurface(unsigned char *data, const gfxIntSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
virtual ~gfxQuartzSurface();

Просмотреть файл

@ -14,8 +14,10 @@
#include "nsUnicharUtils.h"
#include "prlong.h"
#include "nsNetUtil.h"
#include "nsICacheService.h"
#include "nsIProtocolHandler.h"
#include "nsIPrincipal.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#include "opentype-sanitiser.h"
@ -330,7 +332,7 @@ gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
static void
StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
const nsAString& aOriginalName,
bool aPrivate, const nsAString& aOriginalName,
nsTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen)
{
if (!aFontEntry->mUserFontData) {
@ -345,6 +347,7 @@ StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
userFontData->mURI = src.mURI;
userFontData->mPrincipal = aProxy->mPrincipal;
}
userFontData->mPrivate = aPrivate;
userFontData->mFormat = src.mFormatFlags;
userFontData->mRealName = aOriginalName;
if (aMetadata) {
@ -484,7 +487,10 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
uint32_t(mGeneration)));
fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
StoreUserFontData(fe, aProxyEntry, nsString(), nullptr, 0);
// For src:local(), we don't care whether the request is from
// a private window as there's no issue of caching resources;
// local fonts are just available all the time.
StoreUserFontData(fe, aProxyEntry, false, nsString(), nullptr, 0);
ReplaceFontEntry(aFamily, aProxyEntry, fe);
return STATUS_LOADED;
} else {
@ -501,17 +507,21 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
currSrc.mFormatFlags)) {
nsIPrincipal *principal = nullptr;
nsresult rv = CheckFontLoad(&currSrc, &principal);
bool bypassCache;
nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache);
if (NS_SUCCEEDED(rv) && principal != nullptr) {
if (!bypassCache) {
// see if we have an existing entry for this source
gfxFontEntry *fe =
UserFontCache::GetFont(currSrc.mURI, principal,
aProxyEntry);
aProxyEntry,
GetPrivateBrowsing());
if (fe) {
ReplaceFontEntry(aFamily, aProxyEntry, fe);
return STATUS_LOADED;
}
}
// record the principal returned by CheckFontLoad,
// for use when creating a channel
@ -529,9 +539,8 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
// sync load font immediately
rv = SyncLoadFontData(aProxyEntry, &currSrc,
buffer, bufferLength);
if (NS_SUCCEEDED(rv) &&
(fe = LoadFont(aFamily, aProxyEntry,
buffer, bufferLength))) {
if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry,
buffer, bufferLength)) {
return STATUS_LOADED;
} else {
LogMessage(aFamily, aProxyEntry,
@ -654,8 +663,8 @@ gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily,
// newly-created font entry
fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
fe->mLanguageOverride = aProxy->mLanguageOverride;
StoreUserFontData(fe, aProxy, originalFullName,
&metadata, metaOrigLen);
StoreUserFontData(fe, aProxy, GetPrivateBrowsing(),
originalFullName, &metadata, metaOrigLen);
#ifdef PR_LOGGING
if (LOG_ENABLED()) {
nsAutoCString fontURI;
@ -741,6 +750,35 @@ gfxUserFontSet::FindFamilyFor(gfxFontEntry* aFontEntry) const
nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
NS_IMPL_ISUPPORTS1(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
PLDHashOperator
gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry,
void* aUserData)
{
return aEntry->mPrivate ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
}
NS_IMETHODIMP
gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
if (!sUserFonts) {
return NS_OK;
}
if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID)) {
sUserFonts->Clear();
} else if (!strcmp(aTopic, "last-pb-context-exited")) {
sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr);
} else {
NS_NOTREACHED("unexpected topic");
}
return NS_OK;
}
bool
gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
{
@ -753,6 +791,10 @@ gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
return false;
}
if (mPrivate != aKey->mPrivate) {
return false;
}
const gfxFontEntry *fe = aKey->mFontEntry;
if (mFontEntry->mItalic != fe->mItalic ||
mFontEntry->mWeight != fe->mWeight ||
@ -774,10 +816,20 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
if (!sUserFonts) {
sUserFonts = new nsTHashtable<Entry>;
sUserFonts->Init();
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
if (obs) {
Flusher *flusher = new Flusher;
obs->AddObserver(flusher, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
false);
obs->AddObserver(flusher, "last-pb-context-exited", false);
}
}
gfxUserFontData *data = aFontEntry->mUserFontData;
sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
data->mPrivate));
}
void
@ -791,20 +843,23 @@ gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry)
gfxUserFontData *data = aFontEntry->mUserFontData;
if (data) {
sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry));
sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
data->mPrivate));
}
}
gfxFontEntry*
gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy)
gfxProxyFontEntry *aProxy,
bool aPrivate)
{
if (!sUserFonts) {
return nullptr;
}
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy));
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy,
aPrivate));
if (entry) {
return entry->GetFontEntry();
}

Просмотреть файл

@ -62,6 +62,7 @@ public:
uint32_t mSrcIndex; // index in the rule's source list
uint32_t mFormat; // format hint for the source used, if any
uint32_t mMetaOrigLen; // length needed to decompress metadata
bool mPrivate; // whether font belongs to a private window
};
// initially contains a set of proxy font entry objects, replaced with
@ -203,9 +204,12 @@ public:
// which does not directly track families in the font group's list.
gfxFontFamily *FindFamilyFor(gfxFontEntry *aFontEntry) const;
// check whether the given source is allowed to be loaded
// check whether the given source is allowed to be loaded;
// returns the Principal (for use in the key when caching the loaded font),
// and whether the load should bypass the cache (force-reload).
virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal) = 0;
nsIPrincipal **aPrincipal,
bool *aBypassCache) = 0;
// initialize the process that loads external font data, which upon
// completion will call OnLoadComplete method
@ -250,15 +254,30 @@ public:
static void ForgetFont(gfxFontEntry *aFontEntry);
// Return the gfxFontEntry corresponding to a given URI and principal,
// and the features of the given proxy, or nullptr if none is available
// and the features of the given proxy, or nullptr if none is available.
// The aPrivate flag is set for requests coming from private windows,
// so we can avoid leaking fonts cached in private windows mode out to
// normal windows.
static gfxFontEntry* GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy);
gfxProxyFontEntry *aProxy,
bool aPrivate);
// Clear everything so that we don't leak URIs and Principals.
static void Shutdown();
private:
// Helper that we use to observe the empty-cache notification
// from nsICacheService.
class Flusher : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
Flusher() {}
virtual ~Flusher() {}
};
// Key used to look up entries in the user-font cache.
// Note that key comparison does *not* use the mFontEntry field
// as a whole; it only compares specific fields within the entry
@ -269,12 +288,14 @@ public:
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
gfxFontEntry *mFontEntry;
bool mPrivate;
Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
gfxFontEntry* aFontEntry)
gfxFontEntry* aFontEntry, bool aPrivate)
: mURI(aURI),
mPrincipal(aPrincipal),
mFontEntry(aFontEntry)
mFontEntry(aFontEntry),
mPrivate(aPrivate)
{ }
};
@ -286,13 +307,15 @@ public:
Entry(KeyTypePointer aKey)
: mURI(aKey->mURI),
mPrincipal(aKey->mPrincipal),
mFontEntry(aKey->mFontEntry)
mFontEntry(aKey->mFontEntry),
mPrivate(aKey->mPrivate)
{ }
Entry(const Entry& aOther)
: mURI(aOther.mURI),
mPrincipal(aOther.mPrincipal),
mFontEntry(aOther.mFontEntry)
mFontEntry(aOther.mFontEntry),
mPrivate(aOther.mPrivate)
{ }
~Entry() { }
@ -304,7 +327,7 @@ public:
static PLDHashNumber HashKey(const KeyTypePointer aKey) {
uint32_t principalHash;
aKey->mPrincipal->GetHashValue(&principalHash);
return mozilla::HashGeneric(principalHash,
return mozilla::HashGeneric(principalHash + int(aKey->mPrivate),
nsURIHashKey::HashKey(aKey->mURI),
HashFeatures(aKey->mFontEntry->mFeatureSettings),
mozilla::HashString(aKey->mFontEntry->mFamilyName),
@ -318,6 +341,8 @@ public:
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData);
private:
static uint32_t
HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
@ -332,12 +357,18 @@ public:
// The font entry MUST notify the cache when it is destroyed
// (by calling Forget()).
gfxFontEntry *mFontEntry;
// Whether this font was loaded from a private window.
bool mPrivate;
};
static nsTHashtable<Entry> *sUserFonts;
};
protected:
// Return whether the font set is associated with a private-browsing tab.
virtual bool GetPrivateBrowsing() = 0;
// for a given proxy font entry, attempt to load the next resource
// in the src list
LoadStatus LoadNext(gfxMixedFontFamily *aFamily,

Просмотреть файл

@ -663,7 +663,7 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
}
}
if (!decoder->mFrameIsHidden && decoder->NeedsNewFrame()) {
if (decoder->NeedsNewFrame()) {
/* We know that we need a new frame, so pause input so the decoder
* infrastructure can give it to us.
*/
@ -829,10 +829,12 @@ nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format);
if (decoder->NeedsNewFrame()) {
/* We know that we need a new frame, so pause input so the decoder
* infrastructure can give it to us.
*/
png_process_data_pause(png_ptr, /* save = */ 1);
}
#endif
}

Просмотреть файл

@ -1257,6 +1257,10 @@ RasterImage::InternalAddFrame(uint32_t framenum,
nsAutoPtr<imgFrame> frame(new imgFrame());
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
if (!(mSize.width > 0 && mSize.height > 0))
NS_WARNING("Shouldn't call InternalAddFrame with zero size");
if (!NS_SUCCEEDED(rv))
NS_WARNING("imgFrame::Init should succeed");
NS_ENSURE_SUCCESS(rv, rv);
// We know we are in a decoder. Therefore, we must unlock the previous frame
@ -2752,6 +2756,12 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
if (mDecoded)
return NS_OK;
// If we don't have any bytes to flush to the decoder, we can't do anything.
// mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
// the source data.
if (mBytesDecoded > mSourceData.Length())
return NS_OK;
// mFinishing protects against the case when we enter RequestDecode from
// ShutdownDecoder -- in that case, we're done with the decode, we're just
// not quite ready to admit it. See bug 744309.
@ -2901,6 +2911,12 @@ RasterImage::SyncDecode()
if (mDecoded)
return NS_OK;
// If we don't have any bytes to flush to the decoder, we can't do anything.
// mBytesDecoded can be bigger than mSourceData.Length() if we're not storing
// the source data.
if (mBytesDecoded > mSourceData.Length())
return NS_OK;
// If we have a decoder open with different flags than what we need, shut it
// down
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
@ -3286,6 +3302,8 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
if (mBytesDecoded == mSourceData.Length())
return NS_OK;
MOZ_ASSERT(mBytesDecoded < mSourceData.Length());
// write the proper amount of data
uint32_t bytesToDecode = std::min(aMaxBytes,
mSourceData.Length() - mBytesDecoded);

Просмотреть файл

@ -150,8 +150,10 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
gfxASurface::gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
{
// assert for properties that should be verified by decoders, warn for properties related to bad content
if (!AllowedImageSize(aWidth, aHeight))
if (!AllowedImageSize(aWidth, aHeight)) {
NS_WARNING("Should have legal image size");
return NS_ERROR_FAILURE;
}
mOffset.MoveTo(aX, aY);
mSize.SizeTo(aWidth, aHeight);
@ -162,12 +164,15 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
if (aPaletteDepth != 0) {
// We're creating for a paletted image.
if (aPaletteDepth > 8) {
NS_WARNING("Should have legal palette depth");
NS_ERROR("This Depth is not supported");
return NS_ERROR_FAILURE;
}
// Use the fallible allocator here
mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength());
if (!mPalettedImageData)
NS_WARNING("moz_malloc for paletted image data should succeed");
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
} else {
// For Windows, we must create the device surface first (if we're
@ -195,6 +200,11 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
if (!mImageSurface || mImageSurface->CairoStatus()) {
mImageSurface = nullptr;
// guess
if (!mImageSurface) {
NS_WARNING("Allocation of gfxImageSurface should succeed");
} else if (!mImageSurface->CairoStatus()) {
NS_WARNING("gfxImageSurface should have good CairoStatus");
}
return NS_ERROR_OUT_OF_MEMORY;
}

Просмотреть файл

@ -39,6 +39,7 @@ class ProxyBehaviour
virtual imgStatusTracker& GetStatusTracker() const = 0;
virtual imgRequest* GetOwner() const = 0;
virtual void SetOwner(imgRequest* aOwner) = 0;
virtual bool IsStatic() = 0;
};
class RequestBehaviour : public ProxyBehaviour
@ -63,6 +64,8 @@ class RequestBehaviour : public ProxyBehaviour
}
}
virtual bool IsStatic() { return false; }
private:
// We maintain the following invariant:
// The proxy is registered at most with a single imgRequest as an observer,
@ -78,6 +81,11 @@ class RequestBehaviour : public ProxyBehaviour
mozilla::image::Image*
RequestBehaviour::GetImage() const
{
if (mOwnerHasImage && !mOwner) {
NS_WARNING("If mOwnerHasImage is true mOwner must be true");
MOZ_CRASH();
}
if (!mOwnerHasImage)
return nullptr;
return GetStatusTracker().GetImage();
@ -92,6 +100,11 @@ RequestBehaviour::GetStatusTracker() const
// That's why this method uses mOwner->GetStatusTracker() instead of just
// mOwner->mStatusTracker -- we might have a null mImage and yet have an
// mOwner with a non-null mImage (and a null mStatusTracker pointer).
if (mOwnerHasImage && !mOwner) {
NS_WARNING("If mOwnerHasImage is true mOwner must be true");
MOZ_CRASH();
}
return mOwner->GetStatusTracker();
}
@ -165,6 +178,11 @@ nsresult imgRequestProxy::Init(imgRequest* aOwner,
NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
if (!mBehaviour->IsStatic() && !aOwner) {
NS_WARNING("Non-static imgRequestProxies should be initialized with an owner");
MOZ_CRASH();
}
mBehaviour->SetOwner(aOwner);
mListener = aObserver;
// Make sure to addref mListener before the AddProxy call below, since
@ -213,6 +231,11 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
if (!mBehaviour->IsStatic() && !aNewOwner) {
NS_WARNING("Non-static imgRequestProxies should be only changed to a non-null owner");
MOZ_CRASH();
}
mBehaviour->SetOwner(aNewOwner);
// If we were locked, apply the locks here
@ -562,6 +585,10 @@ NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
{
nsresult result;
imgRequestProxy* proxy;
if (mBehaviour->IsStatic()) {
NS_WARNING("Calling non-static imgRequestProxy::Clone with static mBehaviour");
MOZ_CRASH();
}
result = Clone(aObserver, &proxy);
*aClone = proxy;
return result;
@ -1022,6 +1049,8 @@ public:
MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner.");
}
virtual bool IsStatic() { return true; }
private:
// Our image. We have to hold a strong reference here, because that's normally
// the job of the underlying request.
@ -1051,6 +1080,10 @@ imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
{
nsresult result;
imgRequestProxy* proxy;
if (!mBehaviour->IsStatic()) {
NS_WARNING("Calling static imgRequestProxy::Clone with non-static mBehaviour");
MOZ_CRASH();
}
result = PerformClone(aObserver, NewStaticProxy, &proxy);
*aClone = proxy;
return result;

Просмотреть файл

@ -925,8 +925,15 @@ selfhosted_out_h_deps := \
$(srcdir)/builtin/embedjs.py \
$(NULL)
SELFHOSTED_DEFINES += $(DEFINES) $(ACDEFINES)
ifdef MOZ_DEBUG
SELFHOSTED_DEFINES += $(MOZ_DEBUG_ENABLE_DEFS)
else
SELFHOSTED_DEFINES += $(MOZ_DEBUG_DISABLE_DEFS)
endif
selfhosted.out.h: $(selfhosted_out_h_deps)
$(PYTHON) $(srcdir)/builtin/embedjs.py $(DEFINES) $(ACDEFINES) \
$(PYTHON) $(srcdir)/builtin/embedjs.py $(SELFHOSTED_DEFINES) \
-p '$(CPP)' -m $(srcdir)/js.msg -o $@ $(selfhosting_srcs)
###############################################

Просмотреть файл

@ -1000,6 +1000,7 @@ BytecodeEmitter::isAliasedName(ParseNode *pn)
return script->varIsAliased(pn->pn_cookie.slot());
case Definition::PLACEHOLDER:
case Definition::NAMED_LAMBDA:
case Definition::MISSING:
JS_NOT_REACHED("unexpected dn->kind");
}
return false;
@ -1343,6 +1344,9 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
case Definition::PLACEHOLDER:
return true;
case Definition::MISSING:
JS_NOT_REACHED("missing");
}
/*

Просмотреть файл

@ -6220,7 +6220,7 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
bool spread = false, missingTrailingComma = false;
unsigned index = 0;
for (; ; index++) {
if (index == StackSpace::ARGS_LENGTH_MAX) {
if (index == JSObject::NELEMENTS_LIMIT) {
report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
return null();
}

Просмотреть файл

@ -170,7 +170,6 @@ class MinorCollectionTracer : public JSTracer
{
public:
Nursery *nursery;
JSRuntime *runtime;
AutoTraceSession session;
/*
@ -195,18 +194,17 @@ class MinorCollectionTracer : public JSTracer
MinorCollectionTracer(JSRuntime *rt, Nursery *nursery)
: JSTracer(),
nursery(nursery),
runtime(rt),
session(runtime, MinorCollecting),
session(rt, MinorCollecting),
head(NULL),
tail(&head),
savedNeedsBarrier(runtime->needsBarrier()),
disableStrictProxyChecking(runtime)
savedNeedsBarrier(rt->needsBarrier()),
disableStrictProxyChecking(rt)
{
JS_TracerInit(this, runtime, Nursery::MinorGCCallback);
JS_TracerInit(this, rt, Nursery::MinorGCCallback);
eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
runtime->gcNumber++;
runtime->setNeedsBarrier(false);
rt->gcNumber++;
rt->setNeedsBarrier(false);
for (ZonesIter zone(rt); !zone.done(); zone.next())
zone->saveNeedsBarrier(false);
}
@ -222,11 +220,16 @@ class MinorCollectionTracer : public JSTracer
} /* namespace js */
static AllocKind
GetObjectAllocKindForCopy(JSObject *obj)
GetObjectAllocKindForCopy(JSRuntime *rt, JSObject *obj)
{
if (obj->isArray()) {
JS_ASSERT(obj->numFixedSlots() == 0);
size_t nelements = obj->getDenseInitializedLength();
/* Use minimal size object if we are just going to copy the pointer. */
if (!IsInsideNursery(rt, (void *)obj->getElementsHeader()))
return FINALIZE_OBJECT0_BACKGROUND;
size_t nelements = obj->getDenseCapacity();
return GetBackgroundAllocKind(GetGCArrayKind(nelements));
}
@ -262,7 +265,7 @@ void *
js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
{
Zone *zone = src->zone();
AllocKind dstKind = GetObjectAllocKindForCopy(src);
AllocKind dstKind = GetObjectAllocKindForCopy(trc->runtime, src);
JSObject *dst = static_cast<JSObject *>(allocateFromTenured(zone, dstKind));
if (!dst)
MOZ_CRASH();
@ -326,6 +329,7 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
ObjectElements *srcHeader = src->getElementsHeader();
ObjectElements *dstHeader;
/* TODO Bug 874151: Prefer to put element data inline if we have space. */
if (!isInside(srcHeader)) {
JS_ASSERT(src->elements == dst->elements);
hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
@ -348,14 +352,13 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
return;
}
size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->initializedLength;
size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
/* 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;
}
@ -364,7 +367,6 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
if (!dstHeader)
MOZ_CRASH();
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
dstHeader->capacity = srcHeader->initializedLength;
dst->elements = dstHeader->elements();
}
@ -488,6 +490,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
comp->markAllInitialShapeTableEntries(&trc);
}
markStoreBuffer(&trc);
rt->newObjectCache.clearNurseryObjects(rt);
/*
* Most of the work is done here. This loop iterates over objects that have

Просмотреть файл

@ -843,7 +843,7 @@ TypedArrayStoreType(ArrayBufferView::ViewType viewType)
/*****************************************************************************/
typedef Vector<PropertyName*,1> LabelVector;
typedef Vector<MBasicBlock*,16> CaseVector;
typedef Vector<MBasicBlock*,8> BlockVector;
// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
// the course of an ModuleCompiler object's lifetime, many FunctionCompiler
@ -1546,7 +1546,6 @@ class FunctionCompiler
typedef HashMap<PropertyName*, Local> LocalMap;
private:
typedef Vector<MBasicBlock*, 2> BlockVector;
typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap;
typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap;
typedef Vector<ParseNode*, 4> NodeStack;
@ -2019,42 +2018,48 @@ class FunctionCompiler
return true;
}
void joinIf(MBasicBlock *joinBlock)
bool appendThenBlock(BlockVector *thenBlocks) {
if (!curBlock_)
return true;
return thenBlocks->append(curBlock_);
}
void joinIf(const BlockVector &thenBlocks, MBasicBlock *joinBlock)
{
if (!joinBlock)
return;
if (curBlock_) {
curBlock_->end(MGoto::New(joinBlock));
joinBlock->addPredecessor(curBlock_);
JS_ASSERT_IF(curBlock_, thenBlocks.back() == curBlock_);
for (size_t i = 0; i < thenBlocks.length(); i++) {
thenBlocks[i]->end(MGoto::New(joinBlock));
joinBlock->addPredecessor(thenBlocks[i]);
}
curBlock_ = joinBlock;
mirGraph().moveBlockToEnd(curBlock_);
}
MBasicBlock *switchToElse(MBasicBlock *elseBlock)
void switchToElse(MBasicBlock *elseBlock)
{
if (!elseBlock)
return NULL;
MBasicBlock *thenEnd = curBlock_;
return;
curBlock_ = elseBlock;
mirGraph().moveBlockToEnd(curBlock_);
return thenEnd;
}
bool joinIfElse(MBasicBlock *thenEnd)
bool joinIfElse(const BlockVector &thenBlocks)
{
if (!curBlock_ && !thenEnd)
if (!curBlock_ && thenBlocks.empty())
return true;
MBasicBlock *pred = curBlock_ ? curBlock_ : thenEnd;
MBasicBlock *pred = curBlock_ ? curBlock_ : thenBlocks[0];
MBasicBlock *join;
if (!newBlock(pred, &join))
return false;
if (curBlock_)
curBlock_->end(MGoto::New(join));
if (thenEnd)
thenEnd->end(MGoto::New(join));
if (curBlock_ && thenEnd)
join->addPredecessor(thenEnd);
for (size_t i = 0; i < thenBlocks.length(); i++) {
thenBlocks[i]->end(MGoto::New(join));
if (pred == curBlock_ || i > 0)
join->addPredecessor(thenBlocks[i]);
}
curBlock_ = join;
return true;
}
@ -2244,7 +2249,7 @@ class FunctionCompiler
return true;
}
bool startSwitchDefault(MBasicBlock *switchBlock, CaseVector *cases, MBasicBlock **defaultBlock)
bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock)
{
if (!startSwitchCase(switchBlock, defaultBlock))
return false;
@ -2264,7 +2269,7 @@ class FunctionCompiler
return true;
}
bool joinSwitch(MBasicBlock *switchBlock, const CaseVector &cases, MBasicBlock *defaultBlock)
bool joinSwitch(MBasicBlock *switchBlock, const BlockVector &cases, MBasicBlock *defaultBlock)
{
ParseNode *pn = breakableStack_.popCopy();
if (!switchBlock)
@ -3751,8 +3756,12 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType))
return false;
BlockVector thenBlocks(f.cx());
if (!f.appendThenBlock(&thenBlocks))
return false;
f.pushPhiInput(thenDef);
MBasicBlock *thenEnd = f.switchToElse(elseBlock);
f.switchToElse(elseBlock);
MDefinition *elseDef;
Type elseType;
@ -3760,7 +3769,7 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
return false;
f.pushPhiInput(elseDef);
if (!f.joinIfElse(thenEnd))
if (!f.joinIfElse(thenBlocks))
return false;
*def = f.popPhiOutput();
@ -4243,6 +4252,13 @@ CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels
static bool
CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
{
// Handle if/else-if chains using iteration instead of recursion. This
// avoids blowing the C stack quota for long if/else-if chains and also
// creates fewer MBasicBlocks at join points (by creating one join block
// for the entire if/else-if chain).
BlockVector thenBlocks(f.cx());
recurse:
JS_ASSERT(ifStmt->isKind(PNK_IF));
ParseNode *cond = TernaryKid1(ifStmt);
ParseNode *thenStmt = TernaryKid2(ifStmt);
@ -4263,13 +4279,23 @@ CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
if (!CheckStatement(f, thenStmt))
return false;
if (!f.appendThenBlock(&thenBlocks))
return false;
if (!elseStmt) {
f.joinIf(elseBlock);
f.joinIf(thenBlocks, elseBlock);
} else {
MBasicBlock *thenEnd = f.switchToElse(elseBlock);
f.switchToElse(elseBlock);
if (elseStmt->isKind(PNK_IF)) {
ifStmt = elseStmt;
goto recurse;
}
if (!CheckStatement(f, elseStmt))
return false;
if (!f.joinIfElse(thenEnd))
if (!f.joinIfElse(thenBlocks))
return false;
}
@ -4374,7 +4400,7 @@ CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
return false;
CaseVector cases(f.cx());
BlockVector cases(f.cx());
if (!cases.resize(tableLength))
return false;

Просмотреть файл

@ -1373,26 +1373,33 @@ BacktrackingAllocator::minimalInterval(const LiveInterval *interval, bool *pfixe
return minimalDef(interval, reg.ins());
}
bool fixed = false, minimal = false;
for (UsePositionIterator iter = interval->usesBegin(); iter != interval->usesEnd(); iter++) {
LUse *use = iter->use;
switch (use->policy()) {
case LUse::FIXED:
if (pfixed)
*pfixed = true;
return minimalUse(interval, insData[iter->pos].ins());
if (fixed)
return false;
fixed = true;
if (minimalUse(interval, insData[iter->pos].ins()))
minimal = true;
break;
case LUse::REGISTER:
if (pfixed)
*pfixed = false;
return minimalUse(interval, insData[iter->pos].ins());
if (minimalUse(interval, insData[iter->pos].ins()))
minimal = true;
break;
default:
break;
}
}
return false;
if (pfixed)
*pfixed = fixed;
return minimal;
}
size_t
@ -1648,8 +1655,9 @@ BacktrackingAllocator::splitAtAllRegisterUses(LiveInterval *interval)
CodePosition from = inputOf(ins);
CodePosition to = iter->pos.next();
// Watch for duplicate register use positions.
if (newIntervals.empty() || newIntervals.back()->end() != to) {
// Use the same interval for duplicate use positions, except when
// the uses are fixed (they may require incompatible registers).
if (newIntervals.empty() || newIntervals.back()->end() != to || iter->use->policy() == LUse::FIXED) {
if (!addLiveInterval(newIntervals, vreg, from, to))
return false;
}

Просмотреть файл

@ -2434,3 +2434,21 @@ BaselineCompiler::emit_JSOP_ARGUMENTS()
frame.push(R0);
return true;
}
bool
BaselineCompiler::emit_JSOP_REST()
{
frame.syncStack(0);
RootedTypeObject type(cx, types::TypeScript::InitObject(cx, script, pc, JSProto_Array));
if (!type)
return false;
masm.movePtr(ImmGCPtr(type), R0.scratchReg());
ICRest_Fallback::Compiler stubCompiler(cx);
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
return false;
frame.push(R0);
return true;
}

Просмотреть файл

@ -152,6 +152,7 @@ namespace ion {
_(JSOP_EXCEPTION) \
_(JSOP_DEBUGGER) \
_(JSOP_ARGUMENTS) \
_(JSOP_REST) \
_(JSOP_TOID) \
_(JSOP_TABLESWITCH) \
_(JSOP_ITER) \

Просмотреть файл

@ -318,6 +318,12 @@ ICStub::trace(JSTracer *trc)
MarkObject(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter");
break;
}
case ICStub::GetProp_ListBaseShadowed: {
ICGetProp_ListBaseShadowed *propStub = toGetProp_ListBaseShadowed();
MarkShape(trc, &propStub->shape(), "baseline-getproplistbaseshadowed-stub-shape");
MarkString(trc, &propStub->name(), "baseline-getproplistbaseshadowed-stub-name");
break;
}
case ICStub::GetProp_CallScripted: {
ICGetProp_CallScripted *callStub = toGetProp_CallScripted();
MarkShape(trc, &callStub->shape(), "baseline-getpropcallscripted-stub-shape");
@ -614,6 +620,7 @@ ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, La
kind == ICStub::GetProp_CallNative || kind == ICStub::GetProp_CallListBaseNative ||
kind == ICStub::Call_ScriptedApplyArguments ||
kind == ICStub::GetProp_CallListBaseWithGenerationNative ||
kind == ICStub::GetProp_ListBaseShadowed ||
kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
// Guard on bit in frame that indicates if the SPS frame was pushed in the first
@ -3042,7 +3049,7 @@ GetListBaseProto(JSObject *obj)
static void
GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
Address checkProxyHandlerAddr,
Address checkExpandoShapeAddr,
Address *checkExpandoShapeAddr,
Address *expandoAndGenerationAddr,
Address *generationAddr,
Register scratch,
@ -3060,8 +3067,12 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
masm.loadPtr(checkProxyHandlerAddr, scratch);
masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, scratch, checkFailed);
// At this point, if not checking for an expando object, just return.
if (!checkExpandoShapeAddr)
return;
// For the remaining code, we need to reserve some registers to load a value.
// This is ugly, but unvaoidable.
// This is ugly, but unavoidable.
ValueOperand tempVal = listBaseRegSet.takeAnyValue();
masm.pushValue(tempVal);
@ -3092,7 +3103,7 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
// The reference object used to generate this check may not have had an
// expando object at all, in which case the presence of a non-undefined
// expando value in the incoming object is automatically a failure.
masm.loadPtr(checkExpandoShapeAddr, scratch);
masm.loadPtr(*checkExpandoShapeAddr, scratch);
masm.branchPtr(Assembler::Equal, scratch, ImmWord((void*)NULL), &failListBaseCheck);
// Otherwise, ensure that the incoming object has an object for its expando value and that
@ -3117,7 +3128,9 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
static bool
EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleObject holder, MutableHandleShape shape,
bool *checkListBase=NULL, bool *listBaseHasGeneration=NULL)
bool *checkListBase=NULL,
ListBaseShadowsResult *shadowsResult=NULL,
bool *listBaseHasGeneration=NULL)
{
shape.set(NULL);
holder.set(NULL);
@ -3130,20 +3143,23 @@ EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName n
RootedObject checkObj(cx, obj);
if (checkListBase && IsCacheableListBase(obj)) {
JS_ASSERT(listBaseHasGeneration);
JS_ASSERT(shadowsResult);
*checkListBase = isListBase = true;
if (obj->hasUncacheableProto())
return true;
RootedId id(cx, NameToId(name));
ListBaseShadowsResult shadows =
GetListBaseShadowsCheck()(cx, obj, id);
if (shadows == ShadowCheckFailed)
*shadowsResult = GetListBaseShadowsCheck()(cx, obj, id);
if (*shadowsResult == ShadowCheckFailed)
return false;
if (shadows == Shadows)
return true;
*listBaseHasGeneration = (shadows == DoesntShadowUnique);
if (*shadowsResult == Shadows) {
holder.set(obj);
return true;
}
*listBaseHasGeneration = (*shadowsResult == DoesntShadowUnique);
checkObj = GetListBaseProto(obj);
}
@ -5158,11 +5174,14 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
bool isListBase;
bool listBaseHasGeneration;
ListBaseShadowsResult listBaseShadowsResult;
RootedShape shape(cx);
RootedObject holder(cx);
if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isListBase,
&listBaseHasGeneration))
&listBaseShadowsResult, &listBaseHasGeneration))
{
return false;
}
if (!isListBase && !obj->isNative())
return true;
@ -5250,6 +5269,21 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true;
}
// If it's a shadowed listbase proxy property, attach stub to call Proxy::get instead.
if (isListBase && listBaseShadowsResult == Shadows) {
JS_ASSERT(obj == holder);
IonSpew(IonSpew_BaselineIC, " Generating GetProp(ListBaseProxy) stub");
ICGetProp_ListBaseShadowed::Compiler compiler(cx, monitorStub, obj, name,
pc - script->code);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
*attached = true;
return true;
}
return true;
}
@ -5558,14 +5592,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
Label failure;
Label failureLeaveStubFrame;
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch;
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5689,14 +5716,7 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch;
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5763,14 +5783,7 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm,
{
Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch;
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5788,11 +5801,11 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm,
listBaseRegSet.take(BaselineStubReg);
listBaseRegSet.take(objReg);
listBaseRegSet.take(scratch);
Address expandoShapeAddr(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfExpandoShape());
GenerateListBaseChecks(
cx, masm, objReg,
Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfProxyHandler()),
Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfExpandoShape()),
expandoAndGenerationAddr, generationAddr,
&expandoShapeAddr, expandoAndGenerationAddr, generationAddr,
scratch,
listBaseRegSet,
&failure);
@ -5864,7 +5877,7 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm)
return generateStubCode(masm, &internalStructAddress, &generationAddress);
}
ICStub*
ICStub *
ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space)
{
RootedShape shape(cx, obj_->lastProperty());
@ -5899,6 +5912,105 @@ ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space)
expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_,
pcOffset_);
}
ICStub *
ICGetProp_ListBaseShadowed::Compiler::getStub(ICStubSpace *space)
{
RootedShape shape(cx, obj_->lastProperty());
return ICGetProp_ListBaseShadowed::New(space, getStubCode(), firstMonitorStub_,
shape, GetProxyHandler(obj_), name_, pcOffset_);
}
static bool
ProxyGet(JSContext *cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp)
{
RootedId id(cx, NameToId(name));
return Proxy::get(cx, proxy, proxy, id, vp);
}
typedef bool (*ProxyGetFn)(JSContext *cx, HandleObject proxy, HandlePropertyName name,
MutableHandleValue vp);
static const VMFunction ProxyGetInfo = FunctionInfo<ProxyGetFn>(ProxyGet);
bool
ICGetProp_ListBaseShadowed::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1));
// Need to reserve a scratch register, but the scratch register should not be
// BaselineTailCallReg, because it's used for |enterStubFrame| which needs a
// non-BaselineTailCallReg scratch reg.
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
// Unbox.
Register objReg = masm.extractObject(R0, ExtractTemp0);
// Shape guard.
masm.loadPtr(Address(BaselineStubReg, ICGetProp_ListBaseShadowed::offsetOfShape()), scratch);
masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
// Guard for ListObject.
{
GeneralRegisterSet listBaseRegSet(GeneralRegisterSet::All());
listBaseRegSet.take(BaselineStubReg);
listBaseRegSet.take(objReg);
listBaseRegSet.take(scratch);
GenerateListBaseChecks(
cx, masm, objReg,
Address(BaselineStubReg, ICGetProp_ListBaseShadowed::offsetOfProxyHandler()),
/*expandoShapeAddr=*/NULL, /*expandoAndGenerationAddr=*/NULL, /*generationAddr=*/NULL,
scratch,
listBaseRegSet,
&failure);
}
// Call ProxyGet(JSContext *cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp);
// Push a stub frame so that we can perform a non-tail call.
enterStubFrame(masm, scratch);
// Push property name and proxy object.
masm.loadPtr(Address(BaselineStubReg, ICGetProp_ListBaseShadowed::offsetOfName()), scratch);
masm.push(scratch);
masm.push(objReg);
// Don't have to preserve R0 anymore.
regs.add(R0);
// If needed, update SPS Profiler frame entry.
{
Label skipProfilerUpdate;
Register scratch = regs.takeAny();
Register pcIdx = regs.takeAny();
// Check if profiling is enabled.
guardProfilingEnabled(masm, scratch, &skipProfilerUpdate);
// Update profiling entry before leaving function.
masm.load32(Address(BaselineStubReg, ICGetProp_ListBaseShadowed::offsetOfPCOffset()), pcIdx);
masm.spsUpdatePCIdx(&cx->runtime->spsProfiler, pcIdx, scratch);
masm.bind(&skipProfilerUpdate);
regs.add(scratch);
regs.add(pcIdx);
}
if (!callVM(ProxyGetInfo, masm))
return false;
leaveStubFrame(masm);
// Enter type monitor IC to type-check result.
EmitEnterTypeMonitorIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
{
@ -6323,14 +6435,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
EmitStowICValues(masm, 2);
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch;
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
@ -6465,14 +6570,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
EmitStowICValues(masm, 2);
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch;
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
// Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0);
@ -7897,6 +7995,46 @@ ICTypeOf_Typed::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
//
// Rest_Fallback
//
static bool
DoCreateRestParameter(JSContext *cx, BaselineFrame *frame, ICRest_Fallback *stub,
HandleTypeObject type, MutableHandleValue res)
{
FallbackICSpew(cx, stub, "Rest");
unsigned numFormals = frame->numFormalArgs() - 1;
unsigned numActuals = frame->numActualArgs();
unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
JSObject *obj = NewDenseCopiedArray(cx, numRest, frame->actuals() + numFormals, NULL);
if (!obj)
return false;
obj->setType(type);
res.setObject(*obj);
return true;
}
typedef bool(*DoCreateRestParameterFn)(JSContext *cx, BaselineFrame *, ICRest_Fallback *,
HandleTypeObject, MutableHandleValue);
static const VMFunction DoCreateRestParameterInfo =
FunctionInfo<DoCreateRestParameterFn>(DoCreateRestParameter);
bool
ICRest_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
{
EmitRestoreTailCallReg(masm);
masm.push(R0.scratchReg()); // type
masm.push(BaselineStubReg); // stub
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); // frame pointer
return tailCallVM(DoCreateRestParameterInfo, masm);
}
ICProfiler_PushFunction::ICProfiler_PushFunction(IonCode *stubCode, const char *str,
HandleScript script)
: ICStub(ICStub::Profiler_PushFunction, stubCode),
@ -8184,5 +8322,18 @@ ICGetPropCallListBaseNativeCompiler::ICGetPropCallListBaseNativeCompiler(JSConte
JS_ASSERT(GetProxyHandler(obj_)->family() == GetListBaseHandlerFamily());
}
ICGetProp_ListBaseShadowed::ICGetProp_ListBaseShadowed(IonCode *stubCode,
ICStub *firstMonitorStub,
HandleShape shape,
BaseProxyHandler *proxyHandler,
HandlePropertyName name,
uint32_t pcOffset)
: ICMonitoredStub(ICStub::GetProp_ListBaseShadowed, stubCode, firstMonitorStub),
shape_(shape),
proxyHandler_(proxyHandler),
name_(name),
pcOffset_(pcOffset)
{ }
} // namespace ion
} // namespace js

Просмотреть файл

@ -367,6 +367,7 @@ class ICEntry
_(GetProp_CallNative) \
_(GetProp_CallListBaseNative)\
_(GetProp_CallListBaseWithGenerationNative)\
_(GetProp_ListBaseShadowed) \
_(GetProp_ArgumentsLength) \
\
_(SetProp_Fallback) \
@ -387,7 +388,9 @@ class ICEntry
_(InstanceOf_Fallback) \
\
_(TypeOf_Fallback) \
_(TypeOf_Typed)
_(TypeOf_Typed) \
\
_(Rest_Fallback)
#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
IC_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
@ -732,6 +735,7 @@ class ICStub
case GetProp_CallNative:
case GetProp_CallListBaseNative:
case GetProp_CallListBaseWithGenerationNative:
case GetProp_ListBaseShadowed:
case SetProp_CallScripted:
case SetProp_CallNative:
return true;
@ -4357,6 +4361,73 @@ class ICGetPropCallListBaseNativeCompiler : public ICStubCompiler {
ICStub *getStub(ICStubSpace *space);
};
class ICGetProp_ListBaseShadowed : public ICMonitoredStub
{
friend class ICStubSpace;
protected:
HeapPtrShape shape_;
BaseProxyHandler *proxyHandler_;
HeapPtrPropertyName name_;
uint32_t pcOffset_;
ICGetProp_ListBaseShadowed(IonCode *stubCode, ICStub *firstMonitorStub, HandleShape shape,
BaseProxyHandler *proxyHandler, HandlePropertyName name,
uint32_t pcOffset);
public:
static inline ICGetProp_ListBaseShadowed *New(ICStubSpace *space, IonCode *code,
ICStub *firstMonitorStub, HandleShape shape,
BaseProxyHandler *proxyHandler,
HandlePropertyName name, uint32_t pcOffset)
{
if (!code)
return NULL;
return space->allocate<ICGetProp_ListBaseShadowed>(code, firstMonitorStub, shape,
proxyHandler, name, pcOffset);
}
HeapPtrShape &shape() {
return shape_;
}
HeapPtrPropertyName &name() {
return name_;
}
static size_t offsetOfShape() {
return offsetof(ICGetProp_ListBaseShadowed, shape_);
}
static size_t offsetOfProxyHandler() {
return offsetof(ICGetProp_ListBaseShadowed, proxyHandler_);
}
static size_t offsetOfName() {
return offsetof(ICGetProp_ListBaseShadowed, name_);
}
static size_t offsetOfPCOffset() {
return offsetof(ICGetProp_ListBaseShadowed, pcOffset_);
}
class Compiler : public ICStubCompiler {
ICStub *firstMonitorStub_;
RootedObject obj_;
RootedPropertyName name_;
uint32_t pcOffset_;
bool generateStubCode(MacroAssembler &masm);
public:
Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj, HandlePropertyName name,
uint32_t pcOffset)
: ICStubCompiler(cx, ICStub::GetProp_CallNative),
firstMonitorStub_(firstMonitorStub),
obj_(cx, obj),
name_(cx, name),
pcOffset_(pcOffset)
{}
ICStub *getStub(ICStubSpace *space);
};
};
class ICGetProp_ArgumentsLength : public ICStub
{
friend class ICStubSpace;
@ -5411,6 +5482,38 @@ class ICTypeOf_Typed : public ICFallbackStub
};
};
// Rest
// JSOP_REST
class ICRest_Fallback : public ICFallbackStub
{
friend class ICStubSpace;
ICRest_Fallback(IonCode *stubCode)
: ICFallbackStub(ICStub::Rest_Fallback, stubCode)
{ }
public:
static inline ICRest_Fallback *New(ICStubSpace *space, IonCode *code) {
if (!code)
return NULL;
return space->allocate<ICRest_Fallback>(code);
}
class Compiler : public ICStubCompiler {
protected:
bool generateStubCode(MacroAssembler &masm);
public:
Compiler(JSContext *cx)
: ICStubCompiler(cx, ICStub::Rest_Fallback)
{ }
ICStub *getStub(ICStubSpace *space) {
return ICRest_Fallback::New(space, getStubCode());
}
};
};
} // namespace ion
} // namespace js

Просмотреть файл

@ -4859,6 +4859,89 @@ CodeGenerator::visitGetArgument(LGetArgument *lir)
return true;
}
bool
CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
Register temp0, Register temp1, unsigned numFormals,
JSObject *templateObject, const VMFunction &f)
{
// Compute actuals() + numFormals.
size_t actualsOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
masm.movePtr(StackPointer, temp1);
masm.addPtr(Imm32(sizeof(Value) * numFormals + actualsOffset), temp1);
// Compute numActuals - numFormals.
Label emptyLength, joinLength;
masm.movePtr(numActuals, temp0);
masm.branch32(Assembler::LessThanOrEqual, temp0, Imm32(numFormals), &emptyLength);
masm.sub32(Imm32(numFormals), temp0);
masm.jump(&joinLength);
{
masm.bind(&emptyLength);
masm.move32(Imm32(0), temp0);
}
masm.bind(&joinLength);
pushArg(array);
pushArg(ImmGCPtr(templateObject));
pushArg(temp1);
pushArg(temp0);
return callVM(f, lir);
}
typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
HandleObject);
static const VMFunction InitRestParameterInfo =
FunctionInfo<InitRestParameterFn>(InitRestParameter);
bool
CodeGenerator::visitRest(LRest *lir)
{
Register numActuals = ToRegister(lir->numActuals());
Register temp0 = ToRegister(lir->getTemp(0));
Register temp1 = ToRegister(lir->getTemp(1));
Register temp2 = ToRegister(lir->getTemp(2));
unsigned numFormals = lir->mir()->numFormals();
JSObject *templateObject = lir->mir()->templateObject();
Label joinAlloc, failAlloc;
masm.newGCThing(temp2, templateObject, &failAlloc);
masm.initGCThing(temp2, templateObject);
masm.jump(&joinAlloc);
{
masm.bind(&failAlloc);
masm.movePtr(ImmWord((void *)NULL), temp2);
}
masm.bind(&joinAlloc);
return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject,
InitRestParameterInfo);
}
typedef ParallelResult (*ParallelInitRestParameterFn)(ForkJoinSlice *, uint32_t, Value *,
HandleObject, HandleObject,
MutableHandleObject);
static const VMFunction ParallelInitRestParameterInfo =
FunctionInfo<ParallelInitRestParameterFn>(InitRestParameter);
bool
CodeGenerator::visitParRest(LParRest *lir)
{
Register numActuals = ToRegister(lir->numActuals());
Register slice = ToRegister(lir->parSlice());
Register temp0 = ToRegister(lir->getTemp(0));
Register temp1 = ToRegister(lir->getTemp(1));
Register temp2 = ToRegister(lir->getTemp(2));
unsigned numFormals = lir->mir()->numFormals();
JSObject *templateObject = lir->mir()->templateObject();
if (!emitParAllocateGCThing(lir, temp2, slice, temp0, temp1, templateObject))
return false;
return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject,
ParallelInitRestParameterInfo);
}
bool
CodeGenerator::generateAsmJS()
{

Просмотреть файл

@ -211,6 +211,11 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitIteratorEnd(LIteratorEnd *lir);
bool visitArgumentsLength(LArgumentsLength *lir);
bool visitGetArgument(LGetArgument *lir);
bool emitRest(LInstruction *lir, Register array, Register numActuals,
Register temp0, Register temp1, unsigned numFormals,
JSObject *templateObject, const VMFunction &f);
bool visitRest(LRest *lir);
bool visitParRest(LParRest *lir);
bool visitCallSetProperty(LCallSetProperty *ins);
bool visitCallDeleteProperty(LCallDeleteProperty *lir);
bool visitBitNotV(LBitNotV *lir);

Просмотреть файл

@ -1184,6 +1184,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_ARGUMENTS:
return jsop_arguments();
case JSOP_REST:
return jsop_rest();
case JSOP_NOTEARG:
return jsop_notearg();
@ -6864,6 +6867,62 @@ IonBuilder::jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDef
return abort("NYI arguments[]=");
}
bool
IonBuilder::jsop_rest()
{
// We don't know anything about the callee.
if (inliningDepth_ == 0) {
// Get an empty template array.
JSObject *templateObject = getNewArrayTemplateObject(0);
if (!templateObject)
return false;
MArgumentsLength *numActuals = MArgumentsLength::New();
current->add(numActuals);
// Pass in the number of actual arguments, the number of formals (not
// including the rest parameter slot itself), and the template object.
MRest *rest = MRest::New(numActuals, info().nargs() - 1, templateObject);
current->add(rest);
current->push(rest);
return true;
}
// We know the exact number of arguments the callee pushed.
unsigned numActuals = inlinedArguments_.length();
unsigned numFormals = info().nargs() - 1;
unsigned numRest = numActuals - numFormals;
JSObject *templateObject = getNewArrayTemplateObject(numRest);
MNewArray *array = new MNewArray(numRest, templateObject, MNewArray::NewArray_Allocating);
current->add(array);
if (numFormals >= numActuals) {
current->push(array);
return true;
}
MElements *elements = MElements::New(array);
current->add(elements);
// Unroll the argument copy loop. We don't need to do any bounds or hole
// checking here.
MConstant *index;
for (unsigned i = numFormals; i < numActuals; i++) {
index = MConstant::New(Int32Value(i));
current->add(index);
MStoreElement *store = MStoreElement::New(elements, index, inlinedArguments_[i],
/* needsHoleCheck = */ false);
current->add(store);
}
MSetInitializedLength *initLength = MSetInitializedLength::New(elements, index);
current->add(initLength);
current->push(array);
return true;
}
inline types::HeapTypeSet *
GetDefiniteSlot(JSContext *cx, types::StackTypeSet *types, JSAtom *atom)
{

Просмотреть файл

@ -406,6 +406,7 @@ class IonBuilder : public MIRGenerator
bool jsop_arguments_length();
bool jsop_arguments_getelem();
bool jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDefinition *value);
bool jsop_rest();
bool jsop_not();
bool jsop_getprop(HandlePropertyName name);
bool jsop_setprop(HandlePropertyName name);

Просмотреть файл

@ -625,7 +625,8 @@ EmitLoadSlot(MacroAssembler &masm, JSObject *holder, Shape *shape, Register hold
static void
GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
PropertyName *name, Register object, Label *stubFailure)
PropertyName *name, Register object, Label *stubFailure,
bool skipExpandoCheck = false)
{
MOZ_ASSERT(IsCacheableListBase(obj));
@ -639,6 +640,9 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
// Check that object is a ListBase.
masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), stubFailure);
if (skipExpandoCheck)
return;
// For the remaining code, we need to reserve some registers to load a value.
// This is ugly, but unvaoidable.
RegisterSet listBaseRegSet(RegisterSet::All());
@ -1015,6 +1019,118 @@ GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSOb
return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
}
bool
GetPropertyIC::attachListBaseShadowed(JSContext *cx, IonScript *ion, JSObject *obj,
void *returnAddr)
{
JS_ASSERT(!idempotent());
JS_ASSERT(IsCacheableListBase(obj));
JS_ASSERT(output().hasValue());
Label failures;
MacroAssembler masm(cx);
RepatchStubAppender attacher(*this);
masm.setFramePushed(ion->frameSize());
// Guard on the shape of the object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
ImmGCPtr(obj->lastProperty()),
&failures);
// Make sure object is a ListBase proxy
GenerateListBaseChecks(cx, masm, obj, name(), object(), &failures,
/*skipExpandoCheck=*/true);
// saveLive()
masm.PushRegsInMask(liveRegs_);
DebugOnly<uint32_t> initialStack = masm.framePushed();
// Remaining registers should be free, but we need to use |object| still
// so leave it alone.
RegisterSet regSet(RegisterSet::All());
regSet.take(AnyRegister(object()));
// Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
// MutableHandleValue vp)
Register argJSContextReg = regSet.takeGeneral();
Register argProxyReg = regSet.takeGeneral();
Register argIdReg = regSet.takeGeneral();
Register argVpReg = regSet.takeGeneral();
Register scratch = regSet.takeGeneral();
// Push args on stack first so we can take pointers to make handles.
masm.Push(UndefinedValue());
masm.movePtr(StackPointer, argVpReg);
RootedId propId(cx, AtomToId(name()));
masm.Push(propId, scratch);
masm.movePtr(StackPointer, argIdReg);
// Pushing object and receiver. Both are same, so Handle to one is equivalent to
// handle to other.
masm.Push(object());
masm.Push(object());
masm.movePtr(StackPointer, argProxyReg);
masm.loadJSContext(argJSContextReg);
if (!masm.buildOOLFakeExitFrame(returnAddr))
return false;
masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY_GET);
// Make the call.
masm.setupUnalignedABICall(5, scratch);
masm.passABIArg(argJSContextReg);
masm.passABIArg(argProxyReg);
masm.passABIArg(argProxyReg);
masm.passABIArg(argIdReg);
masm.passABIArg(argVpReg);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Proxy::get));
// Test for failure.
Label exception;
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
// Load the outparam vp[0] into output register(s).
masm.loadValue(
Address(StackPointer, IonOOLProxyGetExitFrameLayout::offsetOfResult()),
JSReturnOperand);
Label success;
masm.jump(&success);
// Handle exception case.
masm.bind(&exception);
masm.handleException();
// Handle success case.
masm.bind(&success);
masm.storeCallResultValue(output());
// The next instruction is removing the footer of the exit frame, so there
// is no need for leaveFakeExitFrame.
// Move the StackPointer back to its original location, unwinding the exit frame.
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
JS_ASSERT(masm.framePushed() == initialStack);
// restoreLive()
masm.PopRegsInMask(liveRegs_);
// Success.
attacher.jumpRejoin(masm);
// Failure.
masm.bind(&failures);
attacher.jumpNextStub(masm);
return linkAndAttachStub(cx, masm, attacher, ion, "list base shadowed get");
}
bool
GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
JSObject *holder, HandleShape shape,
@ -1264,12 +1380,15 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
RootedObject checkObj(cx, obj);
if (IsCacheableListBase(obj)) {
RootedId id(cx, NameToId(name));
ListBaseShadowsResult shadows =
GetListBaseShadowsCheck()(cx, obj, id);
ListBaseShadowsResult shadows = GetListBaseShadowsCheck()(cx, obj, id);
if (shadows == ShadowCheckFailed)
return false;
if (shadows == Shadows)
if (shadows == Shadows) {
if (cache.idempotent() || !cache.output().hasValue())
return true;
*isCacheable = true;
return cache.attachListBaseShadowed(cx, ion, obj, returnAddr);
}
if (shadows == DoesntShadowUnique)
// We reset the cache to clear out an existing IC for this object
// (if there is one). The generation is a constant in the generated

Просмотреть файл

@ -542,6 +542,7 @@ class GetPropertyIC : public RepatchIonCache
bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape);
bool attachListBaseShadowed(JSContext *cx, IonScript *ion, JSObject *obj, void *returnAddr);
bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape,
const SafepointIndex *safepointIndex, void *returnAddr);

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше