зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central and inbound
This commit is contained in:
Коммит
6a91572c71
|
@ -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, ×tamp);
|
||||||
|
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(¤tConfig);
|
hal::GetCurrentScreenConfiguration(¤tConfig);
|
||||||
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);
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче