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(); TouchAdapter.start();
Services.obs.addObserver(this, 'remote-browser-frame-shown', false); 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:NextObject', false);
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false); Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
Services.obs.addObserver(this, 'Accessibility:Focus', false); Services.obs.addObserver(this, 'Accessibility:Focus', false);
Utils.win.addEventListener('TabOpen', this); Utils.win.addEventListener('TabOpen', this);
Utils.win.addEventListener('TabClose', this);
Utils.win.addEventListener('TabSelect', this); Utils.win.addEventListener('TabSelect', this);
if (this.readyCallback) { if (this.readyCallback) {
@ -147,9 +149,11 @@ this.AccessFu = {
TouchAdapter.stop(); TouchAdapter.stop();
Utils.win.removeEventListener('TabOpen', this); Utils.win.removeEventListener('TabOpen', this);
Utils.win.removeEventListener('TabClose', this);
Utils.win.removeEventListener('TabSelect', this); Utils.win.removeEventListener('TabSelect', this);
Services.obs.removeObserver(this, 'remote-browser-frame-shown'); 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:NextObject');
Services.obs.removeObserver(this, 'Accessibility:PreviousObject'); Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
Services.obs.removeObserver(this, 'Accessibility:Focus'); Services.obs.removeObserver(this, 'Accessibility:Focus');
@ -280,6 +284,7 @@ this.AccessFu = {
} }
break; break;
case 'remote-browser-frame-shown': case 'remote-browser-frame-shown':
case 'in-process-browser-or-app-frame-shown':
{ {
let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager; let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager;
this._handleMessageManager(mm); this._handleMessageManager(mm);
@ -310,6 +315,16 @@ this.AccessFu = {
this._handleMessageManager(mm); this._handleMessageManager(mm);
break; 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': case 'TabSelect':
{ {
if (this._focused) { if (this._focused) {
@ -464,9 +479,20 @@ var Output = {
aJsonBounds.right - aJsonBounds.left, aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top); aJsonBounds.bottom - aJsonBounds.top);
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0, offsetY: 0 }; 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(); 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, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
var Cc = Components.classes; 'use strict';
var Ci = Components.interfaces;
var Cu = Components.utils; const Ci = Components.interfaces;
var Cr = Components.results; 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'); 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.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: {}, editState: {},
start: function start(aSendMsgFunc) { start: function start() {
try { try {
if (!this._started) { if (!this._started) {
this.sendMsgFunc = aSendMsgFunc || function() {};
Logger.info('EventManager.start', Utils.MozBuildApp); Logger.info('EventManager.start', Utils.MozBuildApp);
this._started = true; 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')); this.present(Presentation.tabStateChanged(null, 'newtab'));
} catch (x) { } 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() { stop: function stop() {
if (!this._started) { if (!this._started) {
return; return;
} }
Logger.info('EventManager.stop', Utils.MozBuildApp); 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; this._started = false;
}
}, },
handleEvent: function handleEvent(aEvent) { 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) { handleAccEvent: function handleAccEvent(aEvent) {
if (Logger.logLevel >= Logger.DEBUG) if (Logger.logLevel >= Logger.DEBUG)
Logger.debug('A11yEvent', Logger.eventToString(aEvent), Logger.debug('A11yEvent', Logger.eventToString(aEvent),
@ -116,7 +138,7 @@ this.EventManager = {
case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED: case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
{ {
let pivot = aEvent.accessible. let pivot = aEvent.accessible.
QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor; QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
let position = pivot.position; let position = pivot.position;
if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME)
break; break;
@ -227,11 +249,6 @@ this.EventManager = {
this.sendMsgFunc("AccessFu:Present", aPresentationData); 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) { onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
let tabstate = ''; let tabstate = '';
@ -269,3 +286,145 @@ this.EventManager = {
Ci.nsISupports, Ci.nsISupports,
Ci.nsIObserver]) 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) match: function BaseTraversalRule_match(aAccessible)
{ {
if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) { if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
return (aAccessible.childCount) ? return (Utils.getMessageManager(aAccessible.DOMNode)) ?
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE : Ci.nsIAccessibleTraversalRule.FILTER_MATCH :
Ci.nsIAccessibleTraversalRule.FILTER_MATCH; Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
} }
if (this._matchFunc) if (this._matchFunc)

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

@ -37,6 +37,9 @@ this.Utils = {
}, },
get win() { get win() {
if (!this._win) {
return null;
}
return this._win.get(); return this._win.get();
}, },
@ -90,6 +93,9 @@ this.Utils = {
}, },
get BrowserApp() { get BrowserApp() {
if (!this.win) {
return null;
}
switch (this.MozBuildApp) { switch (this.MozBuildApp) {
case 'mobile/android': case 'mobile/android':
return this.win.BrowserApp; return this.win.BrowserApp;
@ -103,6 +109,9 @@ this.Utils = {
}, },
get CurrentBrowser() { get CurrentBrowser() {
if (!this.BrowserApp) {
return null;
}
if (this.MozBuildApp == 'b2g') if (this.MozBuildApp == 'b2g')
return this.BrowserApp.contentBrowser; return this.BrowserApp.contentBrowser;
return this.BrowserApp.selectedBrowser; return this.BrowserApp.selectedBrowser;
@ -122,15 +131,27 @@ this.Utils = {
let document = this.CurrentContentDoc; let document = this.CurrentContentDoc;
if (document) { 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; return messageManagers;
}, },
get isContentProcess() {
delete this.isContentProcess;
this.isContentProcess =
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
return this.isContentProcess;
},
getMessageManager: function getMessageManager(aBrowser) { getMessageManager: function getMessageManager(aBrowser) {
try { try {
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner). return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
@ -164,15 +185,12 @@ this.Utils = {
let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument : let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
this.AccRetrieval.getAccessibleFor(aDocument); this.AccRetrieval.getAccessibleFor(aDocument);
while (doc) { return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
try { },
return doc.QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
} catch (x) {
doc = doc.parentDocument;
}
}
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) { logException: function logException(aException) {
try { try {
this.error( let args = [aException.message];
aException.message, args.push.apply(args, aException.stack ? ['\n', aException.stack] :
'(' + aException.fileName + ':' + aException.lineNumber + ')'); ['(' + aException.fileName + ':' + aException.lineNumber + ')']);
this.error.apply(this, args);
} catch (x) { } catch (x) {
this.error(x); this.error(x);
} }
@ -392,15 +411,7 @@ PivotContext.prototype = {
this._accessible.getBounds(objX, objY, objW, objH); this._accessible.getBounds(objX, objY, objW, objH);
// XXX: OOP content provides a screen offset of 0, while in-process provides a real this._bounds = new Rect(objX.value, objY.value, objW.value, objH.value);
// 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);
} }
return this._bounds.clone(); return this._bounds.clone();

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

@ -2,18 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
var Cc = Components.classes; let Ci = Components.interfaces;
var Ci = Components.interfaces; let Cu = Components.utils;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import('resource://gre/modules/accessibility/Utils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/accessibility/EventManager.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm'); 'resource://gre/modules/accessibility/Utils.jsm');
Cu.import('resource://gre/modules/Services.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'); Logger.debug('content-script.js');
let eventManager = null;
function virtualCursorControl(aMessage) { function virtualCursorControl(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) if (Logger.logLevel >= Logger.DEBUG)
Logger.debug(aMessage.name, JSON.stringify(aMessage.json)); Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
@ -52,14 +59,21 @@ function virtualCursorControl(aMessage) {
} }
break; break;
case 'moveToPoint': 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; break;
case 'whereIsIt': case 'whereIsIt':
if (!forwardMessage(vc, aMessage)) { if (!forwardMessage(vc, aMessage)) {
if (!vc.position && aMessage.json.move) if (!vc.position && aMessage.json.move)
vc.moveFirst(TraversalRules.Simple); vc.moveFirst(TraversalRules.Simple);
else else {
EventManager.presentVirtualCursorPosition(vc); sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
}
} }
break; break;
@ -88,10 +102,12 @@ function forwardMessage(aVirtualCursor, aMessage) {
let mm = Utils.getMessageManager(acc.DOMNode); let mm = Utils.getMessageManager(acc.DOMNode);
mm.addMessageListener(aMessage.name, virtualCursorControl); mm.addMessageListener(aMessage.name, virtualCursorControl);
aMessage.json.origin = 'parent'; aMessage.json.origin = 'parent';
if (Utils.isContentProcess) {
// XXX: OOP content's screen offset is 0, // XXX: OOP content's screen offset is 0,
// so we remove the real screen offset here. // so we remove the real screen offset here.
aMessage.json.x -= content.mozInnerScreenX; aMessage.json.x -= content.mozInnerScreenX;
aMessage.json.y -= content.mozInnerScreenY; aMessage.json.y -= content.mozInnerScreenY;
}
mm.sendAsyncMessage(aMessage.name, aMessage.json); mm.sendAsyncMessage(aMessage.name, aMessage.json);
return true; return true;
} }
@ -124,14 +140,14 @@ function activateCurrent(aMessage) {
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode; let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
function dispatchMouseEvent(aEventType) { function dispatchMouseEvent(aEventType) {
let evt = content.document.createEvent("MouseEvents"); let evt = content.document.createEvent('MouseEvents');
evt.initMouseEvent(aEventType, true, true, content, evt.initMouseEvent(aEventType, true, true, content,
x, y, 0, 0, 0, false, false, false, false, 0, null); x, y, 0, 0, 0, false, false, false, false, 0, null);
node.dispatchEvent(evt); node.dispatchEvent(evt);
} }
dispatchMouseEvent("mousedown"); dispatchMouseEvent('mousedown');
dispatchMouseEvent("mouseup"); dispatchMouseEvent('mouseup');
} }
} }
@ -234,20 +250,10 @@ addMessageListener(
addMessageListener('AccessFu:Activate', activateCurrent); addMessageListener('AccessFu:Activate', activateCurrent);
addMessageListener('AccessFu:Scroll', scroll); addMessageListener('AccessFu:Scroll', scroll);
EventManager.start( if (!eventManager) {
function sendMessage(aName, aDetails) { eventManager = new EventManager(this);
sendAsyncMessage(aName, aDetails); }
}); eventManager.start();
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);
}); });
addMessageListener( addMessageListener(
@ -259,15 +265,7 @@ addMessageListener(
removeMessageListener('AccessFu:Activate', activateCurrent); removeMessageListener('AccessFu:Activate', activateCurrent);
removeMessageListener('AccessFu:Scroll', scroll); removeMessageListener('AccessFu:Scroll', scroll);
EventManager.stop(); 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);
}); });
sendAsyncMessage('AccessFu:Ready'); sendAsyncMessage('AccessFu:Ready');

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Text.h" #include "mozilla/dom/Text.h"
#include "nsTextNode.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -19,5 +20,18 @@ Text::SplitText(uint32_t aOffset, ErrorResult& rv)
return newChild.forget().downcast<Text>(); 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 dom
} // namespace mozilla } // namespace mozilla

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

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

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

@ -2346,6 +2346,16 @@ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
} }
#endif #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, MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
"Bad readyState"); "Bad readyState");
SetReadyStateInternal(READYSTATE_LOADING); SetReadyStateInternal(READYSTATE_LOADING);

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

@ -3077,3 +3077,15 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
::InvalidateAllFrames(commonAncestor); ::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 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
// WebIDL // WebIDL
static already_AddRefed<nsRange>
Constructor(const mozilla::dom::GlobalObject& global,
mozilla::ErrorResult& aRv);
bool Collapsed() const bool Collapsed() const
{ {
return mIsPositioned && mStartParent == mEndParent && return mIsPositioned && mStartParent == mEndParent &&

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

@ -626,6 +626,8 @@ MOCHITEST_FILES_C= \
badMessageEvent2.eventsource^headers^ \ badMessageEvent2.eventsource^headers^ \
test_object.html \ test_object.html \
test_bug869006.html \ test_bug869006.html \
test_bug868999.html \
test_bug869000.html \
$(NULL) $(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec # 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; mIsScreenCleared = false;
mDisableFragHighP = false; mDisableFragHighP = false;
mDrawCallsSinceLastFlush = 0;
} }
WebGLContext::~WebGLContext() WebGLContext::~WebGLContext()
@ -806,6 +808,7 @@ public:
// Present our screenbuffer, if needed. // Present our screenbuffer, if needed.
context->PresentScreenBuffer(); context->PresentScreenBuffer();
context->mDrawCallsSinceLastFlush = 0;
} }
/** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite, /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,

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

@ -1128,6 +1128,10 @@ protected:
ContextStatus mContextStatus; ContextStatus mContextStatus;
bool mContextLostErrorSet; 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; int mAlreadyGeneratedWarnings;
bool mAlreadyWarnedAboutFakeVertexAttrib0; bool mAlreadyWarnedAboutFakeVertexAttrib0;

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

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

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

@ -448,7 +448,7 @@ public:
bool Muted() const bool Muted() const
{ {
return mMuted; return mMuted & MUTED_BY_CONTENT;
} }
// XPCOM SetMuted() is OK // XPCOM SetMuted() is OK
@ -786,9 +786,9 @@ protected:
void ProcessMediaFragmentURI(); 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 * Suspend (if aPauseForInactiveDocument) or resume element playback and
@ -1009,8 +1009,13 @@ protected:
// 'Pause' method, or playback not yet having started. // 'Pause' method, or playback not yet having started.
WakeLockBoolWrapper mPaused; WakeLockBoolWrapper mPaused;
// True if the sound is muted. enum MutedReasons {
bool mMuted; 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. // True if the sound is being captured.
bool mAudioCaptured; bool mAudioCaptured;
@ -1093,9 +1098,6 @@ protected:
// Audio Channel Type. // Audio Channel Type.
AudioChannelType mAudioChannelType; AudioChannelType mAudioChannelType;
// The audiochannel has been suspended.
bool mChannelSuspended;
// Is this media element playing? // Is this media element playing?
bool mPlayingThroughTheAudioChannel; bool mPlayingThroughTheAudioChannel;

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

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

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

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

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

@ -19,11 +19,11 @@
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptProcessorNode, AudioNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptProcessorNode)
if (tmp->Context()) { if (tmp->Context()) {
tmp->Context()->UnregisterScriptProcessorNode(tmp); 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_BEGIN_INHERITED(ScriptProcessorNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

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

@ -48,6 +48,8 @@ WMFReader::WMFReader(AbstractMediaDecoder* aDecoder)
mVideoWidth(0), mVideoWidth(0),
mVideoHeight(0), mVideoHeight(0),
mVideoStride(0), mVideoStride(0),
mAudioFrameSum(0),
mAudioFrameOffset(0),
mHasAudio(false), mHasAudio(false),
mHasVideo(false), mHasVideo(false),
mCanSeek(false), mCanSeek(false),
@ -605,6 +607,31 @@ GetSampleDuration(IMFSample* aSample)
return HNsToUsecs(duration); 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 bool
WMFReader::DecodeAudioData() WMFReader::DecodeAudioData()
{ {
@ -660,11 +687,32 @@ WMFReader::DecodeAudioData()
memcpy(pcmSamples.get(), data, currentLength); memcpy(pcmSamples.get(), data, currentLength);
buffer->Unlock(); buffer->Unlock();
int64_t offset = mDecoder->GetResource()->Tell(); // We calculate the timestamp and the duration based on the number of audio
int64_t timestamp = HNsToUsecs(timestampHns); // frames we've already played. We don't trust the timestamp stored on the
int64_t duration = GetSampleDuration(sample); // 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, timestamp,
duration, duration,
numFrames, numFrames,

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

@ -91,6 +91,13 @@ private:
uint32_t mVideoHeight; uint32_t mVideoHeight;
uint32_t mVideoStride; 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 mHasAudio;
bool mHasVideo; bool mHasVideo;
bool mCanSeek; bool mCanSeek;

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

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

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

@ -92,6 +92,7 @@ MOCHITEST_FILES = \
test_viewport.html \ test_viewport.html \
zoom-helper.svg \ zoom-helper.svg \
test_zoom.xhtml \ test_zoom.xhtml \
test_bug872812.html \
$(NULL) $(NULL)
ifneq (android,$(MOZ_WIDGET_TOOLKIT)) 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 NS_IMETHODIMP
nsLocationSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, 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 // Shadowing protection. This will go away when nsLocation moves to the new
// bindings. // bindings.
JS::Rooted<jsid> id(cx, aId);
if (wrapper->HasNativeMember(id)) { if (wrapper->HasNativeMember(id)) {
JS_ReportError(cx, "Permission denied to shadow native property"); JS_ReportError(cx, "Permission denied to shadow native property");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;

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

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

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

@ -84,6 +84,11 @@ public:
mMouseDownEventHandlingDocument = aDocument; 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 * Returns the content node that would be focused if aWindow was in an
* active window. This will traverse down the frame hierarchy, starting at * 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 { class nsCloseEvent : public nsRunnable {
nsRefPtr<nsGlobalWindow> mWindow; nsRefPtr<nsGlobalWindow> mWindow;
bool mIndirect;
nsCloseEvent(nsGlobalWindow *aWindow) nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
: mWindow(aWindow) : mWindow(aWindow)
, mIndirect(aIndirect)
{} {}
public: public:
static nsresult static nsresult
PostCloseEvent(nsGlobalWindow* aWindow) { PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow); nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
nsresult rv = NS_DispatchToCurrentThread(ev); nsresult rv = NS_DispatchToCurrentThread(ev);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
aWindow->MaybeForgiveSpamCount(); aWindow->MaybeForgiveSpamCount();
@ -6913,8 +6915,12 @@ public:
} }
NS_IMETHOD Run() { NS_IMETHOD Run() {
if (mWindow) if (mWindow) {
if (mIndirect) {
return PostCloseEvent(mWindow, false);
}
mWindow->ReallyCloseWindow(); mWindow->ReallyCloseWindow();
}
return NS_OK; return NS_OK;
} }
@ -7047,23 +7053,29 @@ nsGlobalWindow::FinalClose()
// Flag that we were closed. // Flag that we were closed.
mIsClosed = true; mIsClosed = true;
JSContext *cx = nsContentUtils::GetCurrentJSContext(); // This stuff is non-sensical but incredibly fragile. The reasons for the
if (cx) { // behavior here don't make sense today and may not have ever made sense,
nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx); // 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
if (currentCX && currentCX == GetContextInternal()) { // wrestling with various download manager tests, frontend code, and possible
currentCX->SetTerminationFunction(CloseWindow, this); // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
mHavePendingClose = true; // testing ground.
return NS_OK; //
} // Here are some quirks that the test suite depends on:
} //
// * When chrome code executes |win|.close(), that close happens immediately,
// We may have plugins on the page that have issued this close from their // along with the accompanying "domwindowclosed" notification. But _only_ if
// event loop and because we currently destroy the plugin window with // |win|'s JSContext is not at the top of the stack. If it is, the close
// frames, we crash. So, if we are called from Javascript, post an event // _must not_ happen immediately.
// to really close the window. //
if (nsContentUtils::IsCallerChrome() || // * If |win|'s JSContext is at the top of the stack, we must complete _two_
NS_FAILED(nsCloseEvent::PostCloseEvent(this))) { // 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(); ReallyCloseWindow();
} else { } else {
mHavePendingClose = true; mHavePendingClose = true;
@ -9714,15 +9726,25 @@ nsGlobalWindow::GetParentInternal()
return NULL; return NULL;
} }
// static
void void
nsGlobalWindow::CloseBlockScriptTerminationFunc(nsISupports *aRef) nsGlobalWindow::UnblockScriptedClosing()
{ {
nsGlobalWindow* pwin = static_cast<nsGlobalWindow*> mBlockScriptedClosingFlag = false;
(static_cast<nsPIDOMWindow*>(aRef));
pwin->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 nsresult
nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, bool aDialog, const nsAString& aOptions, bool aDialog,
@ -9752,6 +9774,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript, NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript,
"Shouldn't have caller context when called noscript"); "Shouldn't have caller context when called noscript");
mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
// Calls to window.open from script should navigate. // Calls to window.open from script should navigate.
MOZ_ASSERT(aCalledNoScript || aNavigate); 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. // so that whatever popup blocker UI the app has will be visible.
if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) { if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) {
mBlockScriptedClosingFlag = true; mBlockScriptedClosingFlag = true;
mContext->SetTerminationFunction(CloseBlockScriptTerminationFunc, closeUnblocker.construct(this);
this);
} }
} }
@ -9918,22 +9941,6 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
return rv; 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 // nsGlobalWindow: Timeout Functions
//***************************************************************************** //*****************************************************************************

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

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

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

@ -27,11 +27,9 @@ class nsIScriptObjectPrincipal;
class nsIDOMWindow; class nsIDOMWindow;
class nsIURI; class nsIURI;
typedef void (*nsScriptTerminationFunc)(nsISupports* aRef);
#define NS_ISCRIPTCONTEXT_IID \ #define NS_ISCRIPTCONTEXT_IID \
{ 0x821c5be9, 0xbf9e, 0x4041, \ { 0xef0c91ce, 0x14f6, 0x41c9, \
{ 0x9f, 0xf2, 0x1f, 0xca, 0x39, 0xf7, 0x89, 0xf3 } } { 0xa5, 0x77, 0xa6, 0xeb, 0xdc, 0x6d, 0x44, 0x7b } }
/* This MUST match JSVERSION_DEFAULT. This version stuff if we don't /* This MUST match JSVERSION_DEFAULT. This version stuff if we don't
know what language we have is a little silly... */ know what language we have is a little silly... */
@ -179,11 +177,10 @@ public:
* A GC may be done if "necessary." * A GC may be done if "necessary."
* This call is necessary if script evaluation is done * This call is necessary if script evaluation is done
* without using the EvaluateScript method. * without using the EvaluateScript method.
* @param aTerminated If true then call termination function if it was * @param aTerminated If true then do script termination handling. Within DOM
* previously set. Within DOM this will always be true, but outside * this will always be true, but outside callers (such as xpconnect) who
* callers (such as xpconnect) who may do script evaluations nested * may do script evaluations nested inside inside DOM script evaluations
* inside DOM script evaluations can pass false to avoid premature * can pass false to avoid premature termination handling.
* calls to the termination function.
* @return NS_OK if the method is successful * @return NS_OK if the method is successful
*/ */
virtual void ScriptEvaluated(bool aTerminated) = 0; virtual void ScriptEvaluated(bool aTerminated) = 0;
@ -196,19 +193,6 @@ public:
virtual nsresult Deserialize(nsIObjectInputStream* aStream, virtual nsresult Deserialize(nsIObjectInputStream* aStream,
JS::MutableHandle<JSScript*> aResult) = 0; 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. * Called to disable/enable script execution in this context.
*/ */

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

@ -1141,7 +1141,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime, bool aGCOnDestruction,
::JS_SetOperationCallback(mContext, DOMOperationCallback); ::JS_SetOperationCallback(mContext, DOMOperationCallback);
} }
mIsInitialized = false; mIsInitialized = false;
mTerminations = nullptr;
mScriptsEnabled = true; mScriptsEnabled = true;
mOperationCallbackTime = 0; mOperationCallbackTime = 0;
mModalStateTime = 0; mModalStateTime = 0;
@ -1156,11 +1155,6 @@ nsJSContext::~nsJSContext()
mNext->mPrev = mPrev; 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; mGlobalObjectRef = nullptr;
DestroyJSContext(); DestroyJSContext();
@ -1289,8 +1283,6 @@ nsJSContext::EvaluateString(const nsAString& aScript,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(ok, NS_OK); NS_ENSURE_TRUE(ok, NS_OK);
nsJSContext::TerminationFuncHolder holder(this);
// Scope the JSAutoCompartment so that it gets destroyed before we pop the // Scope the JSAutoCompartment so that it gets destroyed before we pop the
// cx and potentially call JS_RestoreFrameChain. // cx and potentially call JS_RestoreFrameChain.
XPCAutoRequest ar(mContext); XPCAutoRequest ar(mContext);
@ -1414,7 +1406,6 @@ nsJSContext::ExecuteScript(JSScript* aScriptObject_,
nsCxPusher pusher; nsCxPusher pusher;
pusher.Push(mContext); pusher.Push(mContext);
nsJSContext::TerminationFuncHolder holder(this);
XPCAutoRequest ar(mContext); XPCAutoRequest ar(mContext);
// Scope the JSAutoCompartment so that it gets destroyed before we pop the // Scope the JSAutoCompartment so that it gets destroyed before we pop the
@ -2361,20 +2352,6 @@ nsJSContext::IsContextInitialized()
void void
nsJSContext::ScriptEvaluated(bool aTerminated) 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); JS_MaybeGC(mContext);
if (aTerminated) { 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 bool
nsJSContext::GetScriptsEnabled() nsJSContext::GetScriptsEnabled()
{ {

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

@ -75,8 +75,6 @@ public:
virtual bool IsContextInitialized(); virtual bool IsContextInitialized();
virtual void ScriptEvaluated(bool aTerminated); virtual void ScriptEvaluated(bool aTerminated);
virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
nsIDOMWindow* aRef);
virtual bool GetScriptsEnabled(); virtual bool GetScriptsEnabled();
virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts); virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
@ -186,66 +184,6 @@ private:
JSContext *mContext; JSContext *mContext;
bool mActive; 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 mIsInitialized;
bool mScriptsEnabled; bool mScriptsEnabled;
bool mGCOnDestruction; 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. // Specialization for strings.
// XXXbz we can't pull in FakeDependentString here, because it depends on // 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 // 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. // Victory! We have a JSContext. Now do the things we need a JSContext for.
mAr.construct(cx); 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. // Make sure our JSContext is pushed on the stack.
mCxPusher.Push(cx); mCxPusher.Push(cx);
@ -109,14 +114,6 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
nsresult rv = nsContentUtils::GetSecurityManager()-> nsresult rv = nsContentUtils::GetSecurityManager()->
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr); 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)) { if (NS_FAILED(rv)) {
// Security check failed. We're done here. // Security check failed. We're done here.
return; return;

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

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

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

@ -7710,7 +7710,9 @@ class CGDictionary(CGThing):
selfName = self.makeClassName(d) selfName = self.makeClassName(d)
members = [ClassMember(self.makeMemberName(m[0].identifier.name), members = [ClassMember(self.makeMemberName(m[0].identifier.name),
self.getMemberType(m), 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") ctor = ClassConstructor([], bodyInHeader=True, visibility="public")
methods = [] methods = []
@ -7931,6 +7933,25 @@ class CGDictionary(CGThing):
return trace.define() 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 @staticmethod
def makeIdName(name): def makeIdName(name):
return name + "_id" return name + "_id"

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

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

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

@ -7,6 +7,7 @@ tail =
[test_add_put.js] [test_add_put.js]
[test_add_twice_failure.js] [test_add_twice_failure.js]
[test_advance.js] [test_advance.js]
[test_autoIncrement.js]
[test_autoIncrement_indexes.js] [test_autoIncrement_indexes.js]
[test_clear.js] [test_clear.js]
[test_complex_keyPaths.js] [test_complex_keyPaths.js]
@ -18,6 +19,7 @@ tail =
[test_cursor_update_updates_indexes.js] [test_cursor_update_updates_indexes.js]
[test_cursors.js] [test_cursors.js]
[test_deleteDatabase.js] [test_deleteDatabase.js]
[test_deleteDatabase_interactions.js]
[test_event_source.js] [test_event_source.js]
[test_getAll.js] [test_getAll.js]
[test_global_data.js] [test_global_data.js]
@ -43,6 +45,7 @@ tail =
[test_overlapping_transactions.js] [test_overlapping_transactions.js]
[test_put_get_values.js] [test_put_get_values.js]
[test_put_get_values_autoIncrement.js] [test_put_get_values_autoIncrement.js]
[test_readonly_transactions.js]
[test_remove_index.js] [test_remove_index.js]
[test_remove_objectStore.js] [test_remove_objectStore.js]
[test_request_readyState.js] [test_request_readyState.js]
@ -56,5 +59,7 @@ tail =
[test_transaction_lifetimes.js] [test_transaction_lifetimes.js]
[test_transaction_lifetimes_nested.js] [test_transaction_lifetimes_nested.js]
[test_transaction_ordering.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! # 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> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7"> <script type="text/javascript;version=1.7" src="unit/test_autoIncrement.js"></script>
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="helpers.js"></script> <script type="text/javascript;version=1.7" src="helpers.js"></script>
</head> </head>
<body onload="runTest();"></body> <body onload="runTest();"></body>

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

@ -9,64 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7"> <script type="text/javascript;version=1.7" src="unit/test_deleteDatabase_interactions.js"></script>
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="helpers.js"></script> <script type="text/javascript;version=1.7" src="helpers.js"></script>
</head> </head>

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

@ -9,175 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7"> <script type="text/javascript;version=1.7" src="unit/test_readonly_transactions.js"></script>
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="helpers.js"></script> <script type="text/javascript;version=1.7" src="helpers.js"></script>
</head> </head>

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

@ -9,66 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7"> <script type="text/javascript;version=1.7" src="unit/test_unique_index_update.js"></script>
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="helpers.js"></script> <script type="text/javascript;version=1.7" src="helpers.js"></script>
</head> </head>

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

@ -9,98 +9,7 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7"> <script type="text/javascript;version=1.7" src="unit/test_writer_starvation.js"></script>
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="helpers.js"></script> <script type="text/javascript;version=1.7" src="helpers.js"></script>
</head> </head>

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

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

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

@ -38,8 +38,8 @@ function todo(condition, name, diag) {
dump("TODO: ", diag); dump("TODO: ", diag);
} }
function info(msg) { function info(name, message) {
do_print(msg); do_print(name);
} }
function run_test() { function run_test() {
@ -193,6 +193,19 @@ function gc()
Components.utils.forceCC(); 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 = { var SpecialPowers = {
isMainProcess: function() { isMainProcess: function() {
return Components.classes["@mozilla.org/xre/app-info;1"] 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_put.js]
[test_add_twice_failure.js] [test_add_twice_failure.js]
[test_advance.js] [test_advance.js]
[test_autoIncrement.js]
[test_autoIncrement_indexes.js] [test_autoIncrement_indexes.js]
[test_clear.js] [test_clear.js]
[test_complex_keyPaths.js] [test_complex_keyPaths.js]
@ -18,6 +19,7 @@ tail =
[test_cursor_update_updates_indexes.js] [test_cursor_update_updates_indexes.js]
[test_cursors.js] [test_cursors.js]
[test_deleteDatabase.js] [test_deleteDatabase.js]
[test_deleteDatabase_interactions.js]
[test_event_source.js] [test_event_source.js]
[test_getAll.js] [test_getAll.js]
[test_global_data.js] [test_global_data.js]
@ -47,6 +49,7 @@ tail =
[test_overlapping_transactions.js] [test_overlapping_transactions.js]
[test_put_get_values.js] [test_put_get_values.js]
[test_put_get_values_autoIncrement.js] [test_put_get_values_autoIncrement.js]
[test_readonly_transactions.js]
[test_remove_index.js] [test_remove_index.js]
[test_remove_objectStore.js] [test_remove_objectStore.js]
[test_request_readyState.js] [test_request_readyState.js]
@ -61,5 +64,7 @@ tail =
[test_transaction_lifetimes.js] [test_transaction_lifetimes.js]
[test_transaction_lifetimes_nested.js] [test_transaction_lifetimes_nested.js]
[test_transaction_ordering.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! # When adding files here please also update ipc/unit/xpcshell.ini!

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

@ -40,7 +40,6 @@ MOCHITEST_FILES = \
test_peerConnection_bug827843.html \ test_peerConnection_bug827843.html \
test_peerConnection_bug834153.html \ test_peerConnection_bug834153.html \
test_peerConnection_bug835370.html \ test_peerConnection_bug835370.html \
test_peerConnection_bug840344.html \
head.js \ head.js \
mediaStreamPlayback.js \ mediaStreamPlayback.js \
pc.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. * liability, trademark and document use rules apply.
*/ */
[Constructor]
interface Range { interface Range {
[Throws] [Throws]
readonly attribute Node startContainer; readonly attribute Node startContainer;

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

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

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

@ -4977,13 +4977,12 @@ nsEditor::FinalizeSelection()
nsCOMPtr<nsIPresShell> presShell = GetPresShell(); nsCOMPtr<nsIPresShell> presShell = GetPresShell();
NS_ENSURE_TRUE_VOID(presShell); NS_ENSURE_TRUE_VOID(presShell);
nsRefPtr<nsCaret> caret = presShell->GetCaret();
if (caret) {
caret->SetIgnoreUserModify(true);
}
selCon->SetCaretEnabled(false); selCon->SetCaretEnabled(false);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE_VOID(fm);
fm->UpdateCaretForCaretBrowsingMode();
if (!HasIndependentSelection()) { if (!HasIndependentSelection()) {
// If this editor doesn't have an independent selection, i.e., it must // 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 // 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) 205",
"Adreno (TM) 320", "Adreno (TM) 320",
"PowerVR SGX 530", "PowerVR SGX 530",
"PowerVR SGX 540" "PowerVR SGX 540",
"NVIDIA Tegra"
}; };
mRenderer = RendererOther; mRenderer = RendererOther;

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

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

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

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

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

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

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

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

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

@ -13,6 +13,7 @@
#include "mozilla/layers/LayersSurfaces.h" #include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/layers/SharedPlanarYCbCrImage.h" #include "mozilla/layers/SharedPlanarYCbCrImage.h"
#include "mozilla/layers/SharedRGBImage.h" #include "mozilla/layers/SharedRGBImage.h"
#include "nsXULAppAPI.h"
#ifdef DEBUG #ifdef DEBUG
#include "prenv.h" #include "prenv.h"
@ -85,6 +86,16 @@ ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfxIntSize& aSize,
return true; 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; nsRefPtr<gfxSharedImageSurface> buffer;
if (!AllocSharedImageSurface(aSize, aContent, if (!AllocSharedImageSurface(aSize, aContent,
getter_AddRefs(buffer))) { getter_AddRefs(buffer))) {
@ -122,6 +133,9 @@ ISurfaceAllocator::DestroySharedSurface(SurfaceDescriptor* aSurface)
break; break;
case SurfaceDescriptor::TSurfaceDescriptorD3D10: case SurfaceDescriptor::TSurfaceDescriptorD3D10:
break; break;
case SurfaceDescriptor::TMemoryImage:
delete [] (unsigned char *)aSurface->get_MemoryImage().data();
break;
case SurfaceDescriptor::Tnull_t: case SurfaceDescriptor::Tnull_t:
case SurfaceDescriptor::T__None: case SurfaceDescriptor::T__None:
break; break;

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

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

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

@ -32,10 +32,22 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfxIntSize& aSize,
ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode aMode, ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode aMode,
const SurfaceDescriptor& aSurface) const SurfaceDescriptor& aSurface)
{ {
if (SurfaceDescriptor::TShmem != aSurface.type()) { if (aSurface.type() == SurfaceDescriptor::TShmem) {
return nullptr;
}
return gfxSharedQuartzSurface::Open(aSurface.get_Shmem()); 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 /*static*/ bool

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

@ -358,7 +358,7 @@ ShadowLayerForwarder::UpdateTextureIncremental(CompositableClient* aCompositable
{ {
MOZ_ASSERT(aCompositable); MOZ_ASSERT(aCompositable);
MOZ_ASSERT(aCompositable->GetIPDLActor()); MOZ_ASSERT(aCompositable->GetIPDLActor());
mTxn->AddPaint(OpPaintTextureIncremental(nullptr, aCompositable->GetIPDLActor(), mTxn->AddNoSwapPaint(OpPaintTextureIncremental(nullptr, aCompositable->GetIPDLActor(),
aTextureId, aTextureId,
aDescriptor, aDescriptor,
aUpdatedRegion, aUpdatedRegion,
@ -530,6 +530,16 @@ ShadowLayerForwarder::OpenDescriptor(OpenMode aMode,
rgbFormat); rgbFormat);
return surf.forget(); 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: default:
NS_ERROR("unexpected SurfaceDescriptor type!"); NS_ERROR("unexpected SurfaceDescriptor type!");
return nullptr; return nullptr;
@ -730,7 +740,7 @@ ShadowLayerForwarder::CreatedIncrementalBuffer(CompositableClient* aCompositable
const TextureInfo& aTextureInfo, const TextureInfo& aTextureInfo,
const nsIntRect& aBufferRect) const nsIntRect& aBufferRect)
{ {
mTxn->AddPaint(OpCreatedIncrementalTexture(nullptr, aCompositable->GetIPDLActor(), mTxn->AddNoSwapPaint(OpCreatedIncrementalTexture(nullptr, aCompositable->GetIPDLActor(),
aTextureInfo, aBufferRect)); aTextureInfo, aBufferRect));
} }

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

@ -777,7 +777,7 @@ CompositorOGL::BeginFrame(const Rect *aClipRectIn, const gfxMatrix& aTransform,
// sent atomically with rotation changes // sent atomically with rotation changes
nsIntRect intRect; nsIntRect intRect;
mWidget->GetClientBounds(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 // thread, and undoes all the care we take with layers txns being
// sent atomically with rotation changes // sent atomically with rotation changes
mWidget->GetClientBounds(rect); mWidget->GetClientBounds(rect);
rect.x = rect.y = 0;
} }
} }
WorldTransformRect(rect); 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> already_AddRefed<gfxASurface>
gfxQuartzSurface::CreateSimilarSurface(gfxContentType aType, gfxQuartzSurface::CreateSimilarSurface(gfxContentType aType,
const gfxIntSize& aSize) const gfxIntSize& aSize)

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

@ -20,6 +20,7 @@ public:
gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false); gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
gfxQuartzSurface(cairo_surface_t *csurf, 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 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(); virtual ~gfxQuartzSurface();

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

@ -14,8 +14,10 @@
#include "nsUnicharUtils.h" #include "nsUnicharUtils.h"
#include "prlong.h" #include "prlong.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsICacheService.h"
#include "nsIProtocolHandler.h" #include "nsIProtocolHandler.h"
#include "nsIPrincipal.h" #include "nsIPrincipal.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "opentype-sanitiser.h" #include "opentype-sanitiser.h"
@ -330,7 +332,7 @@ gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily,
static void static void
StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy, StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
const nsAString& aOriginalName, bool aPrivate, const nsAString& aOriginalName,
nsTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen) nsTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen)
{ {
if (!aFontEntry->mUserFontData) { if (!aFontEntry->mUserFontData) {
@ -345,6 +347,7 @@ StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
userFontData->mURI = src.mURI; userFontData->mURI = src.mURI;
userFontData->mPrincipal = aProxy->mPrincipal; userFontData->mPrincipal = aProxy->mPrincipal;
} }
userFontData->mPrivate = aPrivate;
userFontData->mFormat = src.mFormatFlags; userFontData->mFormat = src.mFormatFlags;
userFontData->mRealName = aOriginalName; userFontData->mRealName = aOriginalName;
if (aMetadata) { if (aMetadata) {
@ -484,7 +487,10 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
uint32_t(mGeneration))); uint32_t(mGeneration)));
fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings); fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
fe->mLanguageOverride = aProxyEntry->mLanguageOverride; 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); ReplaceFontEntry(aFamily, aProxyEntry, fe);
return STATUS_LOADED; return STATUS_LOADED;
} else { } else {
@ -501,17 +507,21 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
currSrc.mFormatFlags)) { currSrc.mFormatFlags)) {
nsIPrincipal *principal = nullptr; nsIPrincipal *principal = nullptr;
nsresult rv = CheckFontLoad(&currSrc, &principal); bool bypassCache;
nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache);
if (NS_SUCCEEDED(rv) && principal != nullptr) { if (NS_SUCCEEDED(rv) && principal != nullptr) {
if (!bypassCache) {
// see if we have an existing entry for this source // see if we have an existing entry for this source
gfxFontEntry *fe = gfxFontEntry *fe =
UserFontCache::GetFont(currSrc.mURI, principal, UserFontCache::GetFont(currSrc.mURI, principal,
aProxyEntry); aProxyEntry,
GetPrivateBrowsing());
if (fe) { if (fe) {
ReplaceFontEntry(aFamily, aProxyEntry, fe); ReplaceFontEntry(aFamily, aProxyEntry, fe);
return STATUS_LOADED; return STATUS_LOADED;
} }
}
// record the principal returned by CheckFontLoad, // record the principal returned by CheckFontLoad,
// for use when creating a channel // for use when creating a channel
@ -529,9 +539,8 @@ gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily,
// sync load font immediately // sync load font immediately
rv = SyncLoadFontData(aProxyEntry, &currSrc, rv = SyncLoadFontData(aProxyEntry, &currSrc,
buffer, bufferLength); buffer, bufferLength);
if (NS_SUCCEEDED(rv) && if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry,
(fe = LoadFont(aFamily, aProxyEntry, buffer, bufferLength)) {
buffer, bufferLength))) {
return STATUS_LOADED; return STATUS_LOADED;
} else { } else {
LogMessage(aFamily, aProxyEntry, LogMessage(aFamily, aProxyEntry,
@ -654,8 +663,8 @@ gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily,
// newly-created font entry // newly-created font entry
fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings); fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
fe->mLanguageOverride = aProxy->mLanguageOverride; fe->mLanguageOverride = aProxy->mLanguageOverride;
StoreUserFontData(fe, aProxy, originalFullName, StoreUserFontData(fe, aProxy, GetPrivateBrowsing(),
&metadata, metaOrigLen); originalFullName, &metadata, metaOrigLen);
#ifdef PR_LOGGING #ifdef PR_LOGGING
if (LOG_ENABLED()) { if (LOG_ENABLED()) {
nsAutoCString fontURI; nsAutoCString fontURI;
@ -741,6 +750,35 @@ gfxUserFontSet::FindFamilyFor(gfxFontEntry* aFontEntry) const
nsTHashtable<gfxUserFontSet::UserFontCache::Entry>* nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
gfxUserFontSet::UserFontCache::sUserFonts = nullptr; 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 bool
gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
{ {
@ -753,6 +791,10 @@ gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
return false; return false;
} }
if (mPrivate != aKey->mPrivate) {
return false;
}
const gfxFontEntry *fe = aKey->mFontEntry; const gfxFontEntry *fe = aKey->mFontEntry;
if (mFontEntry->mItalic != fe->mItalic || if (mFontEntry->mItalic != fe->mItalic ||
mFontEntry->mWeight != fe->mWeight || mFontEntry->mWeight != fe->mWeight ||
@ -774,10 +816,20 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
if (!sUserFonts) { if (!sUserFonts) {
sUserFonts = new nsTHashtable<Entry>; sUserFonts = new nsTHashtable<Entry>;
sUserFonts->Init(); 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; gfxUserFontData *data = aFontEntry->mUserFontData;
sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry)); sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
data->mPrivate));
} }
void void
@ -791,20 +843,23 @@ gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry)
gfxUserFontData *data = aFontEntry->mUserFontData; gfxUserFontData *data = aFontEntry->mUserFontData;
if (data) { if (data) {
sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry)); sUserFonts->RemoveEntry(Key(data->mURI, data->mPrincipal, aFontEntry,
data->mPrivate));
} }
} }
gfxFontEntry* gfxFontEntry*
gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI, gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal, nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy) gfxProxyFontEntry *aProxy,
bool aPrivate)
{ {
if (!sUserFonts) { if (!sUserFonts) {
return nullptr; return nullptr;
} }
Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy)); Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy,
aPrivate));
if (entry) { if (entry) {
return entry->GetFontEntry(); return entry->GetFontEntry();
} }

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

@ -62,6 +62,7 @@ public:
uint32_t mSrcIndex; // index in the rule's source list uint32_t mSrcIndex; // index in the rule's source list
uint32_t mFormat; // format hint for the source used, if any uint32_t mFormat; // format hint for the source used, if any
uint32_t mMetaOrigLen; // length needed to decompress metadata 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 // 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. // which does not directly track families in the font group's list.
gfxFontFamily *FindFamilyFor(gfxFontEntry *aFontEntry) const; 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, virtual nsresult CheckFontLoad(const gfxFontFaceSrc *aFontFaceSrc,
nsIPrincipal **aPrincipal) = 0; nsIPrincipal **aPrincipal,
bool *aBypassCache) = 0;
// initialize the process that loads external font data, which upon // initialize the process that loads external font data, which upon
// completion will call OnLoadComplete method // completion will call OnLoadComplete method
@ -250,15 +254,30 @@ public:
static void ForgetFont(gfxFontEntry *aFontEntry); static void ForgetFont(gfxFontEntry *aFontEntry);
// Return the gfxFontEntry corresponding to a given URI and principal, // 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, static gfxFontEntry* GetFont(nsIURI *aSrcURI,
nsIPrincipal *aPrincipal, nsIPrincipal *aPrincipal,
gfxProxyFontEntry *aProxy); gfxProxyFontEntry *aProxy,
bool aPrivate);
// Clear everything so that we don't leak URIs and Principals. // Clear everything so that we don't leak URIs and Principals.
static void Shutdown(); static void Shutdown();
private: 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. // Key used to look up entries in the user-font cache.
// Note that key comparison does *not* use the mFontEntry field // Note that key comparison does *not* use the mFontEntry field
// as a whole; it only compares specific fields within the entry // as a whole; it only compares specific fields within the entry
@ -269,12 +288,14 @@ public:
nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIPrincipal> mPrincipal;
gfxFontEntry *mFontEntry; gfxFontEntry *mFontEntry;
bool mPrivate;
Key(nsIURI* aURI, nsIPrincipal* aPrincipal, Key(nsIURI* aURI, nsIPrincipal* aPrincipal,
gfxFontEntry* aFontEntry) gfxFontEntry* aFontEntry, bool aPrivate)
: mURI(aURI), : mURI(aURI),
mPrincipal(aPrincipal), mPrincipal(aPrincipal),
mFontEntry(aFontEntry) mFontEntry(aFontEntry),
mPrivate(aPrivate)
{ } { }
}; };
@ -286,13 +307,15 @@ public:
Entry(KeyTypePointer aKey) Entry(KeyTypePointer aKey)
: mURI(aKey->mURI), : mURI(aKey->mURI),
mPrincipal(aKey->mPrincipal), mPrincipal(aKey->mPrincipal),
mFontEntry(aKey->mFontEntry) mFontEntry(aKey->mFontEntry),
mPrivate(aKey->mPrivate)
{ } { }
Entry(const Entry& aOther) Entry(const Entry& aOther)
: mURI(aOther.mURI), : mURI(aOther.mURI),
mPrincipal(aOther.mPrincipal), mPrincipal(aOther.mPrincipal),
mFontEntry(aOther.mFontEntry) mFontEntry(aOther.mFontEntry),
mPrivate(aOther.mPrivate)
{ } { }
~Entry() { } ~Entry() { }
@ -304,7 +327,7 @@ public:
static PLDHashNumber HashKey(const KeyTypePointer aKey) { static PLDHashNumber HashKey(const KeyTypePointer aKey) {
uint32_t principalHash; uint32_t principalHash;
aKey->mPrincipal->GetHashValue(&principalHash); aKey->mPrincipal->GetHashValue(&principalHash);
return mozilla::HashGeneric(principalHash, return mozilla::HashGeneric(principalHash + int(aKey->mPrivate),
nsURIHashKey::HashKey(aKey->mURI), nsURIHashKey::HashKey(aKey->mURI),
HashFeatures(aKey->mFontEntry->mFeatureSettings), HashFeatures(aKey->mFontEntry->mFeatureSettings),
mozilla::HashString(aKey->mFontEntry->mFamilyName), mozilla::HashString(aKey->mFontEntry->mFamilyName),
@ -318,6 +341,8 @@ public:
gfxFontEntry* GetFontEntry() const { return mFontEntry; } gfxFontEntry* GetFontEntry() const { return mFontEntry; }
static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData);
private: private:
static uint32_t static uint32_t
HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) { HashFeatures(const nsTArray<gfxFontFeature>& aFeatures) {
@ -332,12 +357,18 @@ public:
// The font entry MUST notify the cache when it is destroyed // The font entry MUST notify the cache when it is destroyed
// (by calling Forget()). // (by calling Forget()).
gfxFontEntry *mFontEntry; gfxFontEntry *mFontEntry;
// Whether this font was loaded from a private window.
bool mPrivate;
}; };
static nsTHashtable<Entry> *sUserFonts; static nsTHashtable<Entry> *sUserFonts;
}; };
protected: 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 // for a given proxy font entry, attempt to load the next resource
// in the src list // in the src list
LoadStatus LoadNext(gfxMixedFontFamily *aFamily, 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 /* We know that we need a new frame, so pause input so the decoder
* infrastructure can give it to us. * 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); 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 /* We know that we need a new frame, so pause input so the decoder
* infrastructure can give it to us. * infrastructure can give it to us.
*/ */
png_process_data_pause(png_ptr, /* save = */ 1); png_process_data_pause(png_ptr, /* save = */ 1);
}
#endif #endif
} }

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

@ -1257,6 +1257,10 @@ RasterImage::InternalAddFrame(uint32_t framenum,
nsAutoPtr<imgFrame> frame(new imgFrame()); nsAutoPtr<imgFrame> frame(new imgFrame());
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth); 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); NS_ENSURE_SUCCESS(rv, rv);
// We know we are in a decoder. Therefore, we must unlock the previous frame // We know we are in a decoder. Therefore, we must unlock the previous frame
@ -2752,6 +2756,12 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
if (mDecoded) if (mDecoded)
return NS_OK; 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 // mFinishing protects against the case when we enter RequestDecode from
// ShutdownDecoder -- in that case, we're done with the decode, we're just // ShutdownDecoder -- in that case, we're done with the decode, we're just
// not quite ready to admit it. See bug 744309. // not quite ready to admit it. See bug 744309.
@ -2901,6 +2911,12 @@ RasterImage::SyncDecode()
if (mDecoded) if (mDecoded)
return NS_OK; 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 // If we have a decoder open with different flags than what we need, shut it
// down // down
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) { if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
@ -3286,6 +3302,8 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
if (mBytesDecoded == mSourceData.Length()) if (mBytesDecoded == mSourceData.Length())
return NS_OK; return NS_OK;
MOZ_ASSERT(mBytesDecoded < mSourceData.Length());
// write the proper amount of data // write the proper amount of data
uint32_t bytesToDecode = std::min(aMaxBytes, uint32_t bytesToDecode = std::min(aMaxBytes,
mSourceData.Length() - mBytesDecoded); 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 */) gfxASurface::gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
{ {
// assert for properties that should be verified by decoders, warn for properties related to bad content // 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; return NS_ERROR_FAILURE;
}
mOffset.MoveTo(aX, aY); mOffset.MoveTo(aX, aY);
mSize.SizeTo(aWidth, aHeight); 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) { if (aPaletteDepth != 0) {
// We're creating for a paletted image. // We're creating for a paletted image.
if (aPaletteDepth > 8) { if (aPaletteDepth > 8) {
NS_WARNING("Should have legal palette depth");
NS_ERROR("This Depth is not supported"); NS_ERROR("This Depth is not supported");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Use the fallible allocator here // Use the fallible allocator here
mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength()); 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); NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
} else { } else {
// For Windows, we must create the device surface first (if we're // 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()) { if (!mImageSurface || mImageSurface->CairoStatus()) {
mImageSurface = nullptr; mImageSurface = nullptr;
// guess // 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; return NS_ERROR_OUT_OF_MEMORY;
} }

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

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

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

@ -925,8 +925,15 @@ selfhosted_out_h_deps := \
$(srcdir)/builtin/embedjs.py \ $(srcdir)/builtin/embedjs.py \
$(NULL) $(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) 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) -p '$(CPP)' -m $(srcdir)/js.msg -o $@ $(selfhosting_srcs)
############################################### ###############################################

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

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

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

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

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

@ -170,7 +170,6 @@ class MinorCollectionTracer : public JSTracer
{ {
public: public:
Nursery *nursery; Nursery *nursery;
JSRuntime *runtime;
AutoTraceSession session; AutoTraceSession session;
/* /*
@ -195,18 +194,17 @@ class MinorCollectionTracer : public JSTracer
MinorCollectionTracer(JSRuntime *rt, Nursery *nursery) MinorCollectionTracer(JSRuntime *rt, Nursery *nursery)
: JSTracer(), : JSTracer(),
nursery(nursery), nursery(nursery),
runtime(rt), session(rt, MinorCollecting),
session(runtime, MinorCollecting),
head(NULL), head(NULL),
tail(&head), tail(&head),
savedNeedsBarrier(runtime->needsBarrier()), savedNeedsBarrier(rt->needsBarrier()),
disableStrictProxyChecking(runtime) disableStrictProxyChecking(rt)
{ {
JS_TracerInit(this, runtime, Nursery::MinorGCCallback); JS_TracerInit(this, rt, Nursery::MinorGCCallback);
eagerlyTraceWeakMaps = TraceWeakMapKeysValues; eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
runtime->gcNumber++; rt->gcNumber++;
runtime->setNeedsBarrier(false); rt->setNeedsBarrier(false);
for (ZonesIter zone(rt); !zone.done(); zone.next()) for (ZonesIter zone(rt); !zone.done(); zone.next())
zone->saveNeedsBarrier(false); zone->saveNeedsBarrier(false);
} }
@ -222,11 +220,16 @@ class MinorCollectionTracer : public JSTracer
} /* namespace js */ } /* namespace js */
static AllocKind static AllocKind
GetObjectAllocKindForCopy(JSObject *obj) GetObjectAllocKindForCopy(JSRuntime *rt, JSObject *obj)
{ {
if (obj->isArray()) { if (obj->isArray()) {
JS_ASSERT(obj->numFixedSlots() == 0); 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)); return GetBackgroundAllocKind(GetGCArrayKind(nelements));
} }
@ -262,7 +265,7 @@ void *
js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src) js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
{ {
Zone *zone = src->zone(); Zone *zone = src->zone();
AllocKind dstKind = GetObjectAllocKindForCopy(src); AllocKind dstKind = GetObjectAllocKindForCopy(trc->runtime, src);
JSObject *dst = static_cast<JSObject *>(allocateFromTenured(zone, dstKind)); JSObject *dst = static_cast<JSObject *>(allocateFromTenured(zone, dstKind));
if (!dst) if (!dst)
MOZ_CRASH(); MOZ_CRASH();
@ -326,6 +329,7 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
ObjectElements *srcHeader = src->getElementsHeader(); ObjectElements *srcHeader = src->getElementsHeader();
ObjectElements *dstHeader; ObjectElements *dstHeader;
/* TODO Bug 874151: Prefer to put element data inline if we have space. */
if (!isInside(srcHeader)) { if (!isInside(srcHeader)) {
JS_ASSERT(src->elements == dst->elements); JS_ASSERT(src->elements == dst->elements);
hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader)); hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
@ -348,14 +352,13 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
return; 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. */ /* Unlike other objects, Arrays can have fixed elements. */
if (src->isArray() && nslots <= GetGCKindSlots(dstKind)) { if (src->isArray() && nslots <= GetGCKindSlots(dstKind)) {
dst->setFixedElements(); dst->setFixedElements();
dstHeader = dst->getElementsHeader(); dstHeader = dst->getElementsHeader();
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
dstHeader->capacity = GetGCKindSlots(dstKind) - ObjectElements::VALUES_PER_HEADER;
return; return;
} }
@ -364,7 +367,6 @@ js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKi
if (!dstHeader) if (!dstHeader)
MOZ_CRASH(); MOZ_CRASH();
js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
dstHeader->capacity = srcHeader->initializedLength;
dst->elements = dstHeader->elements(); dst->elements = dstHeader->elements();
} }
@ -488,6 +490,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
comp->markAllInitialShapeTableEntries(&trc); comp->markAllInitialShapeTableEntries(&trc);
} }
markStoreBuffer(&trc); markStoreBuffer(&trc);
rt->newObjectCache.clearNurseryObjects(rt);
/* /*
* Most of the work is done here. This loop iterates over objects that have * 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<PropertyName*,1> LabelVector;
typedef Vector<MBasicBlock*,16> CaseVector; typedef Vector<MBasicBlock*,8> BlockVector;
// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
// the course of an ModuleCompiler object's lifetime, many FunctionCompiler // the course of an ModuleCompiler object's lifetime, many FunctionCompiler
@ -1546,7 +1546,6 @@ class FunctionCompiler
typedef HashMap<PropertyName*, Local> LocalMap; typedef HashMap<PropertyName*, Local> LocalMap;
private: private:
typedef Vector<MBasicBlock*, 2> BlockVector;
typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap; typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap;
typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap; typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap;
typedef Vector<ParseNode*, 4> NodeStack; typedef Vector<ParseNode*, 4> NodeStack;
@ -2019,42 +2018,48 @@ class FunctionCompiler
return true; 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) if (!joinBlock)
return; return;
if (curBlock_) { JS_ASSERT_IF(curBlock_, thenBlocks.back() == curBlock_);
curBlock_->end(MGoto::New(joinBlock)); for (size_t i = 0; i < thenBlocks.length(); i++) {
joinBlock->addPredecessor(curBlock_); thenBlocks[i]->end(MGoto::New(joinBlock));
joinBlock->addPredecessor(thenBlocks[i]);
} }
curBlock_ = joinBlock; curBlock_ = joinBlock;
mirGraph().moveBlockToEnd(curBlock_); mirGraph().moveBlockToEnd(curBlock_);
} }
MBasicBlock *switchToElse(MBasicBlock *elseBlock) void switchToElse(MBasicBlock *elseBlock)
{ {
if (!elseBlock) if (!elseBlock)
return NULL; return;
MBasicBlock *thenEnd = curBlock_;
curBlock_ = elseBlock; curBlock_ = elseBlock;
mirGraph().moveBlockToEnd(curBlock_); mirGraph().moveBlockToEnd(curBlock_);
return thenEnd;
} }
bool joinIfElse(MBasicBlock *thenEnd) bool joinIfElse(const BlockVector &thenBlocks)
{ {
if (!curBlock_ && !thenEnd) if (!curBlock_ && thenBlocks.empty())
return true; return true;
MBasicBlock *pred = curBlock_ ? curBlock_ : thenEnd; MBasicBlock *pred = curBlock_ ? curBlock_ : thenBlocks[0];
MBasicBlock *join; MBasicBlock *join;
if (!newBlock(pred, &join)) if (!newBlock(pred, &join))
return false; return false;
if (curBlock_) if (curBlock_)
curBlock_->end(MGoto::New(join)); curBlock_->end(MGoto::New(join));
if (thenEnd) for (size_t i = 0; i < thenBlocks.length(); i++) {
thenEnd->end(MGoto::New(join)); thenBlocks[i]->end(MGoto::New(join));
if (curBlock_ && thenEnd) if (pred == curBlock_ || i > 0)
join->addPredecessor(thenEnd); join->addPredecessor(thenBlocks[i]);
}
curBlock_ = join; curBlock_ = join;
return true; return true;
} }
@ -2244,7 +2249,7 @@ class FunctionCompiler
return true; return true;
} }
bool startSwitchDefault(MBasicBlock *switchBlock, CaseVector *cases, MBasicBlock **defaultBlock) bool startSwitchDefault(MBasicBlock *switchBlock, BlockVector *cases, MBasicBlock **defaultBlock)
{ {
if (!startSwitchCase(switchBlock, defaultBlock)) if (!startSwitchCase(switchBlock, defaultBlock))
return false; return false;
@ -2264,7 +2269,7 @@ class FunctionCompiler
return true; return true;
} }
bool joinSwitch(MBasicBlock *switchBlock, const CaseVector &cases, MBasicBlock *defaultBlock) bool joinSwitch(MBasicBlock *switchBlock, const BlockVector &cases, MBasicBlock *defaultBlock)
{ {
ParseNode *pn = breakableStack_.popCopy(); ParseNode *pn = breakableStack_.popCopy();
if (!switchBlock) if (!switchBlock)
@ -3751,8 +3756,12 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType)) if (!CheckExpr(f, thenExpr, Use::NoCoercion, &thenDef, &thenType))
return false; return false;
BlockVector thenBlocks(f.cx());
if (!f.appendThenBlock(&thenBlocks))
return false;
f.pushPhiInput(thenDef); f.pushPhiInput(thenDef);
MBasicBlock *thenEnd = f.switchToElse(elseBlock); f.switchToElse(elseBlock);
MDefinition *elseDef; MDefinition *elseDef;
Type elseType; Type elseType;
@ -3760,7 +3769,7 @@ CheckConditional(FunctionCompiler &f, ParseNode *ternary, MDefinition **def, Typ
return false; return false;
f.pushPhiInput(elseDef); f.pushPhiInput(elseDef);
if (!f.joinIfElse(thenEnd)) if (!f.joinIfElse(thenBlocks))
return false; return false;
*def = f.popPhiOutput(); *def = f.popPhiOutput();
@ -4243,6 +4252,13 @@ CheckLabel(FunctionCompiler &f, ParseNode *labeledStmt, LabelVector *maybeLabels
static bool static bool
CheckIf(FunctionCompiler &f, ParseNode *ifStmt) 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)); JS_ASSERT(ifStmt->isKind(PNK_IF));
ParseNode *cond = TernaryKid1(ifStmt); ParseNode *cond = TernaryKid1(ifStmt);
ParseNode *thenStmt = TernaryKid2(ifStmt); ParseNode *thenStmt = TernaryKid2(ifStmt);
@ -4263,13 +4279,23 @@ CheckIf(FunctionCompiler &f, ParseNode *ifStmt)
if (!CheckStatement(f, thenStmt)) if (!CheckStatement(f, thenStmt))
return false; return false;
if (!f.appendThenBlock(&thenBlocks))
return false;
if (!elseStmt) { if (!elseStmt) {
f.joinIf(elseBlock); f.joinIf(thenBlocks, elseBlock);
} else { } else {
MBasicBlock *thenEnd = f.switchToElse(elseBlock); f.switchToElse(elseBlock);
if (elseStmt->isKind(PNK_IF)) {
ifStmt = elseStmt;
goto recurse;
}
if (!CheckStatement(f, elseStmt)) if (!CheckStatement(f, elseStmt))
return false; return false;
if (!f.joinIfElse(thenEnd))
if (!f.joinIfElse(thenBlocks))
return false; return false;
} }
@ -4374,7 +4400,7 @@ CheckSwitch(FunctionCompiler &f, ParseNode *switchStmt)
if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength)) if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
return false; return false;
CaseVector cases(f.cx()); BlockVector cases(f.cx());
if (!cases.resize(tableLength)) if (!cases.resize(tableLength))
return false; return false;

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

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

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

@ -2434,3 +2434,21 @@ BaselineCompiler::emit_JSOP_ARGUMENTS()
frame.push(R0); frame.push(R0);
return true; 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_EXCEPTION) \
_(JSOP_DEBUGGER) \ _(JSOP_DEBUGGER) \
_(JSOP_ARGUMENTS) \ _(JSOP_ARGUMENTS) \
_(JSOP_REST) \
_(JSOP_TOID) \ _(JSOP_TOID) \
_(JSOP_TABLESWITCH) \ _(JSOP_TABLESWITCH) \
_(JSOP_ITER) \ _(JSOP_ITER) \

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

@ -318,6 +318,12 @@ ICStub::trace(JSTracer *trc)
MarkObject(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter"); MarkObject(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter");
break; 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: { case ICStub::GetProp_CallScripted: {
ICGetProp_CallScripted *callStub = toGetProp_CallScripted(); ICGetProp_CallScripted *callStub = toGetProp_CallScripted();
MarkShape(trc, &callStub->shape(), "baseline-getpropcallscripted-stub-shape"); 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::GetProp_CallNative || kind == ICStub::GetProp_CallListBaseNative ||
kind == ICStub::Call_ScriptedApplyArguments || kind == ICStub::Call_ScriptedApplyArguments ||
kind == ICStub::GetProp_CallListBaseWithGenerationNative || kind == ICStub::GetProp_CallListBaseWithGenerationNative ||
kind == ICStub::GetProp_ListBaseShadowed ||
kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative); kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
// Guard on bit in frame that indicates if the SPS frame was pushed in the first // 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 static void
GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object, GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
Address checkProxyHandlerAddr, Address checkProxyHandlerAddr,
Address checkExpandoShapeAddr, Address *checkExpandoShapeAddr,
Address *expandoAndGenerationAddr, Address *expandoAndGenerationAddr,
Address *generationAddr, Address *generationAddr,
Register scratch, Register scratch,
@ -3060,8 +3067,12 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
masm.loadPtr(checkProxyHandlerAddr, scratch); masm.loadPtr(checkProxyHandlerAddr, scratch);
masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, scratch, checkFailed); 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. // 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(); ValueOperand tempVal = listBaseRegSet.takeAnyValue();
masm.pushValue(tempVal); 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 // 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 object at all, in which case the presence of a non-undefined
// expando value in the incoming object is automatically a failure. // 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); masm.branchPtr(Assembler::Equal, scratch, ImmWord((void*)NULL), &failListBaseCheck);
// Otherwise, ensure that the incoming object has an object for its expando value and that // 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 static bool
EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleObject holder, MutableHandleShape shape, MutableHandleObject holder, MutableHandleShape shape,
bool *checkListBase=NULL, bool *listBaseHasGeneration=NULL) bool *checkListBase=NULL,
ListBaseShadowsResult *shadowsResult=NULL,
bool *listBaseHasGeneration=NULL)
{ {
shape.set(NULL); shape.set(NULL);
holder.set(NULL); holder.set(NULL);
@ -3130,20 +3143,23 @@ EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName n
RootedObject checkObj(cx, obj); RootedObject checkObj(cx, obj);
if (checkListBase && IsCacheableListBase(obj)) { if (checkListBase && IsCacheableListBase(obj)) {
JS_ASSERT(listBaseHasGeneration); JS_ASSERT(listBaseHasGeneration);
JS_ASSERT(shadowsResult);
*checkListBase = isListBase = true; *checkListBase = isListBase = true;
if (obj->hasUncacheableProto()) if (obj->hasUncacheableProto())
return true; return true;
RootedId id(cx, NameToId(name)); RootedId id(cx, NameToId(name));
ListBaseShadowsResult shadows = *shadowsResult = GetListBaseShadowsCheck()(cx, obj, id);
GetListBaseShadowsCheck()(cx, obj, id); if (*shadowsResult == ShadowCheckFailed)
if (shadows == ShadowCheckFailed)
return false; return false;
if (shadows == Shadows)
return true;
*listBaseHasGeneration = (shadows == DoesntShadowUnique); if (*shadowsResult == Shadows) {
holder.set(obj);
return true;
}
*listBaseHasGeneration = (*shadowsResult == DoesntShadowUnique);
checkObj = GetListBaseProto(obj); checkObj = GetListBaseProto(obj);
} }
@ -5158,11 +5174,14 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
bool isListBase; bool isListBase;
bool listBaseHasGeneration; bool listBaseHasGeneration;
ListBaseShadowsResult listBaseShadowsResult;
RootedShape shape(cx); RootedShape shape(cx);
RootedObject holder(cx); RootedObject holder(cx);
if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isListBase, if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isListBase,
&listBaseHasGeneration)) &listBaseShadowsResult, &listBaseHasGeneration))
{
return false; return false;
}
if (!isListBase && !obj->isNative()) if (!isListBase && !obj->isNative())
return true; return true;
@ -5250,6 +5269,21 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
return true; 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; return true;
} }
@ -5558,14 +5592,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
Label failure; Label failure;
Label failureLeaveStubFrame; Label failureLeaveStubFrame;
GeneralRegisterSet regs(availableGeneralRegs(1)); GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch; Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
// Guard input is an object. // Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5689,14 +5716,7 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
{ {
Label failure; Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1)); GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch; Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
// Guard input is an object. // Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5763,14 +5783,7 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm,
{ {
Label failure; Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1)); GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch; Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
// Guard input is an object. // Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@ -5788,11 +5801,11 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm,
listBaseRegSet.take(BaselineStubReg); listBaseRegSet.take(BaselineStubReg);
listBaseRegSet.take(objReg); listBaseRegSet.take(objReg);
listBaseRegSet.take(scratch); listBaseRegSet.take(scratch);
Address expandoShapeAddr(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfExpandoShape());
GenerateListBaseChecks( GenerateListBaseChecks(
cx, masm, objReg, cx, masm, objReg,
Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfProxyHandler()), Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfProxyHandler()),
Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfExpandoShape()), &expandoShapeAddr, expandoAndGenerationAddr, generationAddr,
expandoAndGenerationAddr, generationAddr,
scratch, scratch,
listBaseRegSet, listBaseRegSet,
&failure); &failure);
@ -5864,7 +5877,7 @@ ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm)
return generateStubCode(masm, &internalStructAddress, &generationAddress); return generateStubCode(masm, &internalStructAddress, &generationAddress);
} }
ICStub* ICStub *
ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space) ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space)
{ {
RootedShape shape(cx, obj_->lastProperty()); RootedShape shape(cx, obj_->lastProperty());
@ -5899,6 +5912,105 @@ ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space)
expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_, expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_,
pcOffset_); 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 bool
ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm) ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
{ {
@ -6323,14 +6435,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
EmitStowICValues(masm, 2); EmitStowICValues(masm, 2);
GeneralRegisterSet regs(availableGeneralRegs(1)); GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch; Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
// Unbox and shape guard. // Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0); Register objReg = masm.extractObject(R0, ExtractTemp0);
@ -6465,14 +6570,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler &masm)
EmitStowICValues(masm, 2); EmitStowICValues(masm, 2);
GeneralRegisterSet regs(availableGeneralRegs(1)); GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch; Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
if (regs.has(BaselineTailCallReg)) {
regs.take(BaselineTailCallReg);
scratch = regs.takeAny();
regs.add(BaselineTailCallReg);
} else {
scratch = regs.takeAny();
}
// Unbox and shape guard. // Unbox and shape guard.
Register objReg = masm.extractObject(R0, ExtractTemp0); Register objReg = masm.extractObject(R0, ExtractTemp0);
@ -7897,6 +7995,46 @@ ICTypeOf_Typed::Compiler::generateStubCode(MacroAssembler &masm)
return true; 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, ICProfiler_PushFunction::ICProfiler_PushFunction(IonCode *stubCode, const char *str,
HandleScript script) HandleScript script)
: ICStub(ICStub::Profiler_PushFunction, stubCode), : ICStub(ICStub::Profiler_PushFunction, stubCode),
@ -8184,5 +8322,18 @@ ICGetPropCallListBaseNativeCompiler::ICGetPropCallListBaseNativeCompiler(JSConte
JS_ASSERT(GetProxyHandler(obj_)->family() == GetListBaseHandlerFamily()); 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 ion
} // namespace js } // namespace js

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

@ -367,6 +367,7 @@ class ICEntry
_(GetProp_CallNative) \ _(GetProp_CallNative) \
_(GetProp_CallListBaseNative)\ _(GetProp_CallListBaseNative)\
_(GetProp_CallListBaseWithGenerationNative)\ _(GetProp_CallListBaseWithGenerationNative)\
_(GetProp_ListBaseShadowed) \
_(GetProp_ArgumentsLength) \ _(GetProp_ArgumentsLength) \
\ \
_(SetProp_Fallback) \ _(SetProp_Fallback) \
@ -387,7 +388,9 @@ class ICEntry
_(InstanceOf_Fallback) \ _(InstanceOf_Fallback) \
\ \
_(TypeOf_Fallback) \ _(TypeOf_Fallback) \
_(TypeOf_Typed) _(TypeOf_Typed) \
\
_(Rest_Fallback)
#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName; #define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
IC_STUB_KIND_LIST(FORWARD_DECLARE_STUBS) IC_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
@ -732,6 +735,7 @@ class ICStub
case GetProp_CallNative: case GetProp_CallNative:
case GetProp_CallListBaseNative: case GetProp_CallListBaseNative:
case GetProp_CallListBaseWithGenerationNative: case GetProp_CallListBaseWithGenerationNative:
case GetProp_ListBaseShadowed:
case SetProp_CallScripted: case SetProp_CallScripted:
case SetProp_CallNative: case SetProp_CallNative:
return true; return true;
@ -4357,6 +4361,73 @@ class ICGetPropCallListBaseNativeCompiler : public ICStubCompiler {
ICStub *getStub(ICStubSpace *space); 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 class ICGetProp_ArgumentsLength : public ICStub
{ {
friend class ICStubSpace; 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 ion
} // namespace js } // namespace js

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

@ -4859,6 +4859,89 @@ CodeGenerator::visitGetArgument(LGetArgument *lir)
return true; 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 bool
CodeGenerator::generateAsmJS() CodeGenerator::generateAsmJS()
{ {

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

@ -211,6 +211,11 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitIteratorEnd(LIteratorEnd *lir); bool visitIteratorEnd(LIteratorEnd *lir);
bool visitArgumentsLength(LArgumentsLength *lir); bool visitArgumentsLength(LArgumentsLength *lir);
bool visitGetArgument(LGetArgument *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 visitCallSetProperty(LCallSetProperty *ins);
bool visitCallDeleteProperty(LCallDeleteProperty *lir); bool visitCallDeleteProperty(LCallDeleteProperty *lir);
bool visitBitNotV(LBitNotV *lir); bool visitBitNotV(LBitNotV *lir);

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

@ -1184,6 +1184,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_ARGUMENTS: case JSOP_ARGUMENTS:
return jsop_arguments(); return jsop_arguments();
case JSOP_REST:
return jsop_rest();
case JSOP_NOTEARG: case JSOP_NOTEARG:
return jsop_notearg(); return jsop_notearg();
@ -6864,6 +6867,62 @@ IonBuilder::jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDef
return abort("NYI arguments[]="); 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 * inline types::HeapTypeSet *
GetDefiniteSlot(JSContext *cx, types::StackTypeSet *types, JSAtom *atom) GetDefiniteSlot(JSContext *cx, types::StackTypeSet *types, JSAtom *atom)
{ {

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

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

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

@ -625,7 +625,8 @@ EmitLoadSlot(MacroAssembler &masm, JSObject *holder, Shape *shape, Register hold
static void static void
GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj, 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)); MOZ_ASSERT(IsCacheableListBase(obj));
@ -639,6 +640,9 @@ GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
// Check that object is a ListBase. // Check that object is a ListBase.
masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), stubFailure); 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. // For the remaining code, we need to reserve some registers to load a value.
// This is ugly, but unvaoidable. // This is ugly, but unvaoidable.
RegisterSet listBaseRegSet(RegisterSet::All()); RegisterSet listBaseRegSet(RegisterSet::All());
@ -1015,6 +1019,118 @@ GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSOb
return linkAndAttachStub(cx, masm, attacher, ion, attachKind); 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 bool
GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
JSObject *holder, HandleShape shape, JSObject *holder, HandleShape shape,
@ -1264,12 +1380,15 @@ TryAttachNativeGetPropStub(JSContext *cx, IonScript *ion,
RootedObject checkObj(cx, obj); RootedObject checkObj(cx, obj);
if (IsCacheableListBase(obj)) { if (IsCacheableListBase(obj)) {
RootedId id(cx, NameToId(name)); RootedId id(cx, NameToId(name));
ListBaseShadowsResult shadows = ListBaseShadowsResult shadows = GetListBaseShadowsCheck()(cx, obj, id);
GetListBaseShadowsCheck()(cx, obj, id);
if (shadows == ShadowCheckFailed) if (shadows == ShadowCheckFailed)
return false; return false;
if (shadows == Shadows) if (shadows == Shadows) {
if (cache.idempotent() || !cache.output().hasValue())
return true; return true;
*isCacheable = true;
return cache.attachListBaseShadowed(cx, ion, obj, returnAddr);
}
if (shadows == DoesntShadowUnique) if (shadows == DoesntShadowUnique)
// We reset the cache to clear out an existing IC for this object // 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 // (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, bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape); HandleShape shape);
bool attachListBaseShadowed(JSContext *cx, IonScript *ion, JSObject *obj, void *returnAddr);
bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder, bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape, HandleShape shape,
const SafepointIndex *safepointIndex, void *returnAddr); const SafepointIndex *safepointIndex, void *returnAddr);

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