This commit is contained in:
Phil Ringnalda 2013-05-25 22:10:09 -07:00
Родитель 01e9afe170 24157c7636
Коммит a941de44d1
363 изменённых файлов: 6648 добавлений и 4508 удалений

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

@ -20,7 +20,7 @@ interface nsIAccessiblePivotObserver;
* provides traversal methods to move the pivot to next/prev state that complies
* to a given rule.
*/
[scriptable, uuid(15ff23de-879e-47ea-b536-6532466108c5)]
[scriptable, uuid(c2938033-e240-4fe5-9cb6-e7ad649ccd10)]
interface nsIAccessiblePivot : nsISupports
{
const TextBoundaryType CHAR_BOUNDARY = 0;
@ -46,6 +46,11 @@ interface nsIAccessiblePivot : nsISupports
*/
readonly attribute nsIAccessible root;
/**
* The temporary modal root to which traversal is limited to.
*/
attribute nsIAccessible modalRoot;
/**
* The start offset of the text range the pivot points at, otherwise -1.
*/
@ -181,7 +186,7 @@ interface nsIAccessiblePivotObserver : nsISupports
in PivotMoveReason aReason);
};
[scriptable, uuid(307d98b6-dba9-49cf-ba17-ef8b053044eb)]
[scriptable, uuid(366fe92b-44c9-4769-ae40-7c2a075d3b16)]
interface nsIAccessibleTraversalRule : nsISupports
{
/* Ignore this accessible object */
@ -195,6 +200,7 @@ interface nsIAccessibleTraversalRule : nsISupports
const unsigned long PREFILTER_INVISIBLE = 0x00000001;
const unsigned long PREFILTER_OFFSCREEN = 0x00000002;
const unsigned long PREFILTER_NOT_FOCUSABLE = 0x00000004;
const unsigned long PREFILTER_ARIA_HIDDEN = 0x00000008;
/**
* Pre-filter bitfield to filter out obviously ignorable nodes and lighten

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

@ -287,11 +287,10 @@ nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
HWND pluginPort = nullptr;
aFrame->GetPluginPort(&pluginPort);
Accessible* accessible =
nsRefPtr<Accessible> accessible =
new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
pluginPort);
NS_ADDREF(accessible);
return accessible;
return accessible.forget();
#elif MOZ_ACCESSIBILITY_ATK
if (!AtkSocketAccessible::gCanEmbed)

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

@ -6,7 +6,6 @@
#include "nsAccessiblePivot.h"
#include "Accessible-inl.h"
#include "DocAccessible.h"
#include "HyperTextAccessible.h"
#include "nsAccUtils.h"
@ -45,7 +44,7 @@ private:
// nsAccessiblePivot
nsAccessiblePivot::nsAccessiblePivot(Accessible* aRoot) :
mRoot(aRoot), mPosition(nullptr),
mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr),
mStartOffset(-1), mEndOffset(-1)
{
NS_ASSERTION(aRoot, "A root accessible is required");
@ -95,7 +94,7 @@ nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
if (aPosition) {
secondPosition = do_QueryObject(aPosition);
if (!secondPosition || !IsRootDescendant(secondPosition))
if (!secondPosition || !IsDescendantOf(secondPosition, GetActiveRoot()))
return NS_ERROR_INVALID_ARG;
}
@ -109,6 +108,32 @@ nsAccessiblePivot::SetPosition(nsIAccessible* aPosition)
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot)
{
NS_ENSURE_ARG_POINTER(aModalRoot);
NS_IF_ADDREF(*aModalRoot = mModalRoot);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot)
{
nsRefPtr<Accessible> modalRoot;
if (aModalRoot) {
modalRoot = do_QueryObject(aModalRoot);
if (!modalRoot || !IsDescendantOf(modalRoot, mRoot))
return NS_ERROR_INVALID_ARG;
}
mModalRoot.swap(modalRoot);
return NS_OK;
}
NS_IMETHODIMP
nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset)
{
@ -146,7 +171,7 @@ nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible,
return NS_ERROR_INVALID_ARG;
HyperTextAccessible* newPosition = acc->AsHyperText();
if (!newPosition || !IsRootDescendant(newPosition))
if (!newPosition || !IsDescendantOf(newPosition, GetActiveRoot()))
return NS_ERROR_INVALID_ARG;
// Make sure the given offsets don't exceed the character count.
@ -180,9 +205,10 @@ nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule,
*aResult = false;
Accessible* root = GetActiveRoot();
nsRefPtr<Accessible> anchor =
(aArgc > 0) ? do_QueryObject(aAnchor) : mPosition;
if (anchor && (anchor->IsDefunct() || !IsRootDescendant(anchor)))
if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root)))
return NS_ERROR_NOT_IN_TREE;
nsresult rv = NS_OK;
@ -207,9 +233,10 @@ nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule,
*aResult = false;
Accessible* root = GetActiveRoot();
nsRefPtr<Accessible> anchor =
(aArgc > 0) ? do_QueryObject(aAnchor) : mPosition;
if (anchor && (anchor->IsDefunct() || !IsRootDescendant(anchor)))
if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root)))
return NS_ERROR_NOT_IN_TREE;
nsresult rv = NS_OK;
@ -229,11 +256,11 @@ nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
if (mRoot && mRoot->IsDefunct())
return NS_ERROR_NOT_IN_TREE;
Accessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
nsresult rv = NS_OK;
Accessible* accessible = SearchForward(mRoot, aRule, true, &rv);
Accessible* accessible = SearchForward(root, aRule, true, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (accessible)
@ -243,20 +270,21 @@ nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult)
}
NS_IMETHODIMP
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, bool* aResult)
nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule,
bool* aResult)
{
NS_ENSURE_ARG(aResult);
NS_ENSURE_ARG(aRule);
if (mRoot && mRoot->IsDefunct())
return NS_ERROR_NOT_IN_TREE;
Accessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
*aResult = false;
nsresult rv = NS_OK;
Accessible* lastAccessible = mRoot;
Accessible* lastAccessible = root;
Accessible* accessible = nullptr;
// First got to the last accessible in pre-order
// First go to the last accessible in pre-order
while (lastAccessible->HasChildren())
lastAccessible = lastAccessible->LastChild();
@ -302,13 +330,13 @@ nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule,
*aResult = false;
if (mRoot && mRoot->IsDefunct())
return NS_ERROR_NOT_IN_TREE;
Accessible* root = GetActiveRoot();
NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE);
RuleCache cache(aRule);
Accessible* match = nullptr;
Accessible* child = mRoot->ChildAtPoint(aX, aY, Accessible::eDeepestChild);
while (child && mRoot != child) {
Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild);
while (child && root != child) {
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
nsresult rv = cache.ApplyFilter(child, &filtered);
NS_ENSURE_SUCCESS(rv, rv);
@ -360,15 +388,15 @@ nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver)
// Private utility methods
bool
nsAccessiblePivot::IsRootDescendant(Accessible* aAccessible)
nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor)
{
if (!mRoot || mRoot->IsDefunct())
if (!aAncestor || aAncestor->IsDefunct())
return false;
// XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875.
Accessible* accessible = aAccessible;
do {
if (accessible == mRoot)
if (accessible == aAncestor)
return true;
} while ((accessible = accessible->Parent()));
@ -411,7 +439,8 @@ nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
return accessible;
}
while (accessible != mRoot) {
Accessible* root = GetActiveRoot();
while (accessible != root) {
Accessible* parent = accessible->Parent();
int32_t idxInParent = accessible->IndexInParent();
while (idxInParent > 0) {
@ -457,7 +486,8 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible,
*aResult = NS_OK;
// Initial position could be not set, in that case begin search from root.
Accessible* accessible = (!aAccessible) ? mRoot.get() : aAccessible;
Accessible* root = GetActiveRoot();
Accessible* accessible = (!aAccessible) ? root : aAccessible;
RuleCache cache(aRule);
@ -482,7 +512,7 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible,
Accessible* sibling = nullptr;
Accessible* temp = accessible;
do {
if (temp == mRoot)
if (temp == root)
break;
sibling = temp->NextSibling();
@ -549,6 +579,16 @@ RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult)
if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) &&
!(state & states::FOCUSABLE))
return NS_OK;
if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) {
nsIContent* content = aAccessible->GetContent();
if (nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) &&
!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
nsGkAtoms::_false, eCaseMatters)) {
*aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
return NS_OK;
}
}
}
if (mAcceptRolesLength > 0) {

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

@ -9,19 +9,12 @@
#include "nsIAccessiblePivot.h"
#include "Accessible-inl.h"
#include "nsAutoPtr.h"
#include "nsTObserverArray.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
namespace mozilla {
namespace a11y {
class Accessible;
} // namespace a11y
} // namespace mozilla
class nsIAccessibleTraversalRule;
/**
@ -58,9 +51,9 @@ private:
PivotMoveReason aReason);
/*
* Check to see that the given accessible is in the pivot's subtree.
* Check to see that the given accessible is a descendant of given ancestor
*/
bool IsRootDescendant(Accessible* aAccessible);
bool IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor);
/*
@ -79,6 +72,19 @@ private:
bool aSearchCurrent,
nsresult* aResult);
/*
* Get the effective root for this pivot, either the true root or modal root.
*/
Accessible* GetActiveRoot() const
{
if (mModalRoot) {
NS_ENSURE_FALSE(mModalRoot->IsDefunct(), mRoot);
return mModalRoot;
}
return mRoot;
}
/*
* Update the pivot, and notify observers. Return true if it moved.
*/
@ -89,6 +95,11 @@ private:
*/
nsRefPtr<Accessible> mRoot;
/*
* The temporary modal root accessible.
*/
nsRefPtr<Accessible> mModalRoot;
/*
* The current pivot position.
*/

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

@ -28,10 +28,6 @@ this.AccessFu = {
attach: function attach(aWindow) {
Utils.init(aWindow);
this.prefsBranch = Cc['@mozilla.org/preferences-service;1']
.getService(Ci.nsIPrefService).getBranch('accessibility.accessfu.');
this.prefsBranch.addObserver('activate', this, false);
try {
Cc['@mozilla.org/android/bridge;1'].
getService(Ci.nsIAndroidBridge).handleGeckoMessage(
@ -44,21 +40,8 @@ this.AccessFu = {
}
}
try {
this._activatePref = this.prefsBranch.getIntPref('activate');
} catch (x) {
this._activatePref = ACCESSFU_DISABLE;
}
try {
this._notifyOutput = this.prefsBranch.getBoolPref('notify_output');
} catch (x) {
this._notifyOutput = false;
}
this.Input.quickNavMode.updateModes(this.prefsBranch);
this._enableOrDisable();
this._activatePref = new PrefCache(
'accessibility.accessfu.activate', this._enableOrDisable.bind(this), true);
},
/**
@ -76,7 +59,7 @@ this.AccessFu = {
'mozContentEvent', this);
Utils.win.removeEventListener('ContentStart', this);
}
this.prefsBranch.removeObserver('activate', this);
delete this._activatePref;
Utils.uninit();
},
@ -107,6 +90,20 @@ this.AccessFu = {
Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
this.stylesheet = Cu.getWeakReference(stylesheet);
// Populate quicknav modes
this._quicknavModesPref =
new PrefCache(
'accessibility.accessfu.quicknav_modes',
(aName, aValue) => {
this.Input.quickNavMode.updateModes(aValue);
}, true);
// Check for output notification
this._notifyOutputPref =
new PrefCache('accessibility.accessfu.notify_output');
this.Input.start();
Output.start();
TouchAdapter.start();
@ -168,8 +165,9 @@ this.AccessFu = {
_enableOrDisable: function _enableOrDisable() {
try {
if (this._activatePref == ACCESSFU_ENABLE ||
this._systemPref && this._activatePref == ACCESSFU_AUTO)
let activatePref = this._activatePref.value;
if (activatePref == ACCESSFU_ENABLE ||
this._systemPref && activatePref == ACCESSFU_AUTO)
this._enable();
else
this._disable();
@ -211,7 +209,7 @@ this.AccessFu = {
}
}
if (this._notifyOutput) {
if (this._notifyOutputPref.value) {
Services.obs.notifyObservers(null, 'accessfu-output',
JSON.stringify(aPresentationData));
}
@ -272,22 +270,6 @@ this.AccessFu = {
{action: 'whereIsIt', move: true});
}
break;
case 'nsPref:changed':
switch (aData) {
case 'activate':
this._activatePref = this.prefsBranch.getIntPref('activate');
this._enableOrDisable();
break;
case 'quicknav_modes':
this.Input.quickNavMode.updateModes(this.prefsBranch);
break;
case 'notify_output':
this._notifyOutput = this.prefsBranch.getBoolPref('notify_output');
break;
default:
break;
}
break;
case 'remote-browser-frame-shown':
case 'in-process-browser-or-app-frame-shown':
{
@ -738,11 +720,10 @@ var Input = {
this._currentIndex = 0;
},
updateModes: function updateModes(aPrefsBranch) {
try {
this.modes = aPrefsBranch.getCharPref('quicknav_modes').split(',');
} catch (x) {
// Fallback
updateModes: function updateModes(aModes) {
if (aModes) {
this.modes = aModes.split(',');
} else {
this.modes = [];
}
},

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

@ -8,10 +8,13 @@ const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/Geometry.jsm');
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Rect',
'resource://gre/modules/Geometry.jsm');
this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext'];
this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache'];
this.Utils = {
_buildAppMap: {
@ -426,4 +429,55 @@ PivotContext.prototype = {
return true;
}
}
};
this.PrefCache = function PrefCache(aName, aCallback, aRunCallbackNow) {
this.name = aName;
this.callback = aCallback;
let branch = Services.prefs;
this.value = this._getValue(branch);
if (this.callback && aRunCallbackNow) {
try {
this.callback(this.name, this.value);
} catch (x) {
Logger.logException(x);
}
}
branch.addObserver(aName, this, true);
};
PrefCache.prototype = {
_getValue: function _getValue(aBranch) {
if (!this.type) {
this.type = aBranch.getPrefType(this.name);
}
switch (this.type) {
case Ci.nsIPrefBranch.PREF_STRING:
return aBranch.getCharPref(this.name);
case Ci.nsIPrefBranch.PREF_INT:
return aBranch.getIntPref(this.name);
case Ci.nsIPrefBranch.PREF_BOOL:
return aBranch.getBoolPref(this.name);
default:
return null;
}
},
observe: function observe(aSubject, aTopic, aData) {
this.value = this._getValue(aSubject.QueryInterface(Ci.nsIPrefBranch));
if (this.callback) {
try {
this.callback(this.name, this.value);
} catch (x) {
Logger.logException(x);
}
}
},
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference])
};

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

@ -15,29 +15,16 @@ const INCLUDE_CUSTOM = 0x04;
const UTTERANCE_DESC_FIRST = 0;
// Read and observe changes to a setting for utterance order.
let gUtteranceOrder;
let prefsBranch = Cc['@mozilla.org/preferences-service;1']
.getService(Ci.nsIPrefService).getBranch('accessibility.accessfu.');
let observeUtterance = function observeUtterance(aSubject, aTopic, aData) {
try {
gUtteranceOrder = prefsBranch.getIntPref('utterance');
} catch (x) {
gUtteranceOrder = UTTERANCE_DESC_FIRST;
}
};
// Set initial gUtteranceOrder.
observeUtterance();
prefsBranch.addObserver('utterance', observeUtterance, false);
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
let gUtteranceOrder = new PrefCache('accessibility.accessfu.utterance');
var gStringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
getService(Ci.nsIStringBundleService).
createBundle('chrome://global/locale/AccessFu.properties');
this.EXPORTED_SYMBOLS = ['UtteranceGenerator'];
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
/**
* Generates speech utterances from objects, actions and state changes.
@ -88,7 +75,9 @@ this.UtteranceGenerator = {
UtteranceGenerator.genForObject(aAccessible));
};
if (gUtteranceOrder === UTTERANCE_DESC_FIRST) {
let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
if (utteranceOrder === UTTERANCE_DESC_FIRST) {
aContext.newAncestry.forEach(addUtterance);
addUtterance(aContext.accessible);
aContext.subtreePreorder.forEach(addUtterance);
@ -117,7 +106,7 @@ this.UtteranceGenerator = {
let func = this.objectUtteranceFunctions[roleString] ||
this.objectUtteranceFunctions.defaultFunc;
let flags = this.verbosityRoleMap[roleString] || 0;
let flags = this.verbosityRoleMap[roleString] || UTTERANCE_DESC_FIRST;
if (aAccessible.childCount == 0)
flags |= INCLUDE_NAME;
@ -326,8 +315,10 @@ this.UtteranceGenerator = {
_addName: function _addName(utterance, aAccessible, aFlags) {
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
let utteranceOrder = gUtteranceOrder.value || 0;
if (name) {
utterance[gUtteranceOrder === UTTERANCE_DESC_FIRST ?
utterance[utteranceOrder === UTTERANCE_DESC_FIRST ?
"push" : "unshift"](name);
}
},

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

@ -4,11 +4,13 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
// Constants
const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
const PREFILTER_ARIA_HIDDEN = nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN;
const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
const NS_ERROR_NOT_IN_TREE = 0x80780026;
const NS_ERROR_INVALID_ARG = 0x80070057;
////////////////////////////////////////////////////////////////////////////////
// Traversal rules
@ -45,7 +47,7 @@ var ObjectTraversalRule =
return 0;
},
preFilter: PREFILTER_INVISIBLE,
preFilter: PREFILTER_INVISIBLE | PREFILTER_ARIA_HIDDEN,
match: function(aAccessible)
{
@ -260,20 +262,60 @@ function moveVCCoordInvoker(aDocAcc, aX, aY, aIgnoreNoMatch,
}
}
/**
* Change the pivot modalRoot
*
* @param aDocAcc [in] document that manages the virtual cursor
* @param aModalRootAcc [in] accessible of the modal root, or null
* @param aExpectedResult [in] error result expected. 0 if expecting success
*/
function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult)
{
this.invoke = function setModalRootInvoker_invoke()
{
var errorResult = 0;
try {
aDocAcc.virtualCursor.modalRoot = aModalRootAcc;
} catch (x) {
SimpleTest.ok(
x.result, "Unexpected exception when changing modal root: " + x);
errorResult = x.result;
}
SimpleTest.is(errorResult, aExpectedResult,
"Did not get expected result when changing modalRoot");
};
this.getID = function setModalRootInvoker_getID()
{
return "Set modalRoot to " + prettyName(aModalRootAcc);
};
this.eventSeq = [];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
];
}
/**
* Add invokers to a queue to test a rule and an expected sequence of element ids
* or accessible names for that rule in the given document.
*
* @param aQueue [in] event queue in which to push invoker sequence.
* @param aDocAcc [in] the managing document of the virtual cursor we are testing
* @param aRule [in] the traversal rule to use in the invokers
* @param aSequence [in] a sequence of accessible names or element ids to expect with
* the given rule in the given document
* @param aQueue [in] event queue in which to push invoker sequence.
* @param aDocAcc [in] the managing document of the virtual cursor we are
* testing
* @param aRule [in] the traversal rule to use in the invokers
* @param aModalRoot [in] a modal root to use in this traversal sequence
* @param aSequence [in] a sequence of accessible names or element ids to expect
* with the given rule in the given document
*/
function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence)
{
aDocAcc.virtualCursor.position = null;
// Add modal root (if any)
aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
for (var i = 0; i < aSequence.length; i++) {
var invoker =
new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
@ -302,6 +344,9 @@ function queueTraversalSequence(aQueue, aDocAcc, aRule, aSequence)
// No previous more matches for given rule, expect no virtual cursor changes.
aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
// Remove modal root (if any).
aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
}
/**

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

@ -6,19 +6,23 @@
</head>
<body>
<h1 id="heading-1-1">Main Title</h1>
<h2 id="heading-2-1">First Section Title</h2>
<h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
<p id="paragraph-1">
Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
leo, id <a href="#">semper</a> nulla.
</p>
<h2 id="heading-2-2">Second Section Title</h2>
<p id="paragraph-2">
<h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
<p id="paragraph-2" aria-hidden="">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<p id="paragraph-3" aria-hidden="true">
Maybe it was the other <i>George Michael</i>.
You know, the <a href="#">singer-songwriter</a>.
</p>
<iframe
src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
</iframe>
<div id="hide-me">Hide me</div>
<p id="links">
<p id="links" aria-hidden="false">
<a href="http://mozilla.org" title="Link 1 title">Link 1</a>
<a href="http://mozilla.org" title="Link 2 title">Link 2</a>
<a href="http://mozilla.org" title="Link 3 title">Link 3</a>

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

@ -45,12 +45,12 @@
closeBrowserWindow();
}
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule,
queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
['heading-1-1', 'heading-2-1', 'heading-2-2']);
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule,
['Main Title', 'First Section Title', 'Lorem ipsum ',
gQueue, docAcc, ObjectTraversalRule, null,
['Main Title', 'Lorem ipsum ',
'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla. ', 'Second Section Title',
'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
@ -86,6 +86,15 @@
gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false,
HeadersTraversalRule, null));
queueTraversalSequence(
gQueue, docAcc, ObjectTraversalRule,
getAccessible(doc.getElementById('paragraph-1')),
['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
'semper', ' nulla. ']);
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
NS_ERROR_INVALID_ARG));
gQueue.invoke();
}

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

@ -20,12 +20,69 @@ commands (for example `--help`). `cfx` supports the following global options:
-v, --verbose - enable lots of output
</pre>
"Command-specific options" are only
applicable to a subset of the commands.
"Command-specific options" are documented alongside the commands.
## Supported Commands ##
There are five supported cfx commands:
### cfx docs ###
<table>
<colgroup>
<col width="10%">
<col width="90%">
</colgroup>
<tr>
<td>
<a href="dev-guide/cfx-tool.html#cfx-docs"><code>cfx docs</code></a>
</td>
<td>
Display the documentation for the SDK.
</td>
</tr>
<tr>
<td>
<a href="dev-guide/cfx-tool.html#cfx-init"><code>cfx init</code></a>
</td>
<td>
Create a skeleton add-on as a starting point for your own add-on.
</td>
</tr>
<tr>
<td>
<a href="dev-guide/cfx-tool.html#cfx-run"><code>cfx run</code></a>
</td>
<td>
Launch an instance of Firefox with your add-on installed.
</td>
</tr>
<tr>
<td>
<a href="dev-guide/cfx-tool.html#cfx-test"><code>cfx test</code></a>
</td>
<td>
Runs your add-on's unit tests.
</td>
</tr>
<tr>
<td>
<a href="dev-guide/cfx-tool.html#cfx-xpi"><code>cfx xpi</code></a>
</td>
<td>
Package your add-on as an <a href="https://developer.mozilla.org/en/XPI">XPI</a>
file, which is the install file format for Firefox add-ons.
</td>
</tr>
</table>
There are also a number of
[internal commands](dev-guide/cfx-tool.html#internal-commands),
which are more likely to be useful to SDK developers than to add-on developers.
## <a name="cfx-docs">cfx docs</a> ##
This command displays the documentation for the SDK. The documentation is
shipped with the SDK in [Markdown](http://daringfireball.net/projects/markdown/)
@ -46,7 +103,8 @@ This command will regenerate only the HTML page you're reading.
This is useful if you're iteratively editing a single file, and don't want to wait for cfx to
regenerate the complete documentation tree.
### cfx init ####
## <a name="cfx-init">cfx init</a> ##
Create a new directory called "my-addon", change into it, and run `cfx init`.
This command will create an skeleton add-on, as a starting point for your
@ -73,14 +131,13 @@ own add-on development, with the following file structure:
<div style="clear:both"></div>
### cfx run ###
## <a name="cfx-run">cfx run</a> ##
This command is used to run the add-on. Called with no options it looks for a
file called `package.json` in the current directory, loads the corresponding
add-on, and runs it under the version of Firefox it finds in the platform's
default install path.
#### Supported Options #####
### Supported Options ####
You can point `cfx run` at a different `package.json` file using the
`--pkgdir` option, and pass arguments to your add-on using the
@ -190,7 +247,7 @@ See <a href="dev-guide/cfx-tool.html#profiledir">
</table>
#### Experimental Options ####
### Experimental Options ###
<table>
<colgroup>
@ -233,6 +290,25 @@ To launch the application, enter the following command:
</td>
</tr>
<tr>
<td>
<code>-o, --overload-modules</code>
</td>
<td>
<p>In early versions of the SDK, the SDK modules used by an add-on
were themselves included in the add-on. The SDK modules now ship as
part of Firefox. From Firefox 21 onwards, SDK add-ons built with
SDK 1.14 or higher will use the SDK modules that are built into Firefox,
even if the add-on includes its own copies of the SDK modules.</p>
<p>Use this flag to reverse that behavior: if this flag is set and
the add-on includes its own copies of the SDK modules, then the add-on
will use the SDK modules in the add-on, not the ones built into Firefox.</p>
<p>This flag is particularly useful for SDK developers or people working with
the development version of the SDK, who may want to run an add-on using newer
versions of the modules than than those shipping in Firefox.</p>
</td>
</tr>
<tr>
<td>
<code>--templatedir=TEMPLATEDIR</code>
@ -249,7 +325,7 @@ To launch the application, enter the following command:
</table>
#### Internal Options ####
### Internal Options ###
<table>
<colgroup>
@ -291,8 +367,7 @@ To launch the application, enter the following command:
</table>
### cfx test ###
## <a name="cfx-test">cfx test</a> ##
Run available tests for the specified package.
<span class="aside">Note the hyphen after "test" in the module name.
@ -310,7 +385,7 @@ See the
[reference documentation for the `assert` module](modules/sdk/test/assert.html)
for details.
#### Supported Options #####
### Supported Options ###
As with `cfx run` you can use options to control which host application binary
version to use, and to select a profile.
@ -416,7 +491,7 @@ times.
</table>
#### Experimental Options ####
### Experimental Options ###
<table>
<colgroup>
@ -461,16 +536,26 @@ To launch the application, enter the following command:
<tr>
<td>
<code>--use-server</code>
<code>-o, --overload-modules</code>
</td>
<td>
Run tests using a server previously started with <code>cfx develop</code>.
<p>In early versions of the SDK, the SDK modules used by an add-on
were themselves included in the add-on. The SDK modules now ship as
part of Firefox. From Firefox 21 onwards, SDK add-ons built with
SDK 1.14 or higher will use the SDK modules that are built into Firefox,
even if the add-on includes its own copies of the SDK modules.</p>
<p>Use this flag to reverse that behavior: if this flag is set and
the add-on includes its own copies of the SDK modules, then the add-on
will use the SDK modules in the add-on, not the ones built into Firefox.</p>
<p>This flag is particularly useful for SDK developers or people working with
the development version of the SDK, who may want to run an add-on using newer
versions of the modules than than those shipping in Firefox.</p>
</td>
</tr>
</table>
#### Internal Options ####
### Internal Options ###
<table>
<colgroup>
@ -545,8 +630,7 @@ To launch the application, enter the following command:
</table>
### cfx xpi ###
## <a name="cfx-xpi">cfx xpi</a> ##
This tool is used to package your add-on as an
[XPI](https://developer.mozilla.org/en/XPI) file, which is the install file
format for Mozilla add-ons.
@ -557,7 +641,7 @@ the current directory and creates the corresponding XPI file.
Once you have built an XPI file you can distribute your add-on by submitting
it to [addons.mozilla.org](http://addons.mozilla.org).
#### updateURL and updateLink ####
### updateURL and updateLink ###
If you choose to host the XPI yourself you should enable the host application
to find new versions of your add-on.
@ -597,7 +681,7 @@ So if we run the following command:
* an RDF file which embeds `https://example.com/addon/latest` as the value of
`updateLink`.
#### Supported Options ####
### Supported Options ###
As with `cfx run` you can point `cfx` at a different `package.json` file using
the `--pkgdir` option. You can also embed arguments in the XPI using the
@ -675,7 +759,7 @@ add-on whenever it is run.
</table>
#### Experimental Options ####
### Experimental Options ###
<table>
<colgroup>
@ -699,7 +783,7 @@ add-on whenever it is run.
</table>
#### Internal Options ####
### Internal Options ###
<table>
<colgroup>
@ -721,35 +805,7 @@ add-on whenever it is run.
</table>
## Experimental Commands ##
### cfx develop ###
This initiates an instance of a host application in development mode,
and allows you to pipe commands into it from another shell without
having to constantly restart it. Aside from convenience, for SDK
Platform developers this has the added benefit of making it easier to
detect leaks.
For example, in shell A, type:
<pre>
cfx develop
</pre>
In shell B, type:
<pre>
cfx test --use-server
</pre>
This will send `cfx test --use-server` output to shell A. If you repeat the
command in shell B, `cfx test --use-server` output will appear again in shell A
without restarting the host application.
`cfx develop` doesn't take any options.
## Internal Commands ##
## <a name="internal-commands">Internal Commands</a> ##
### cfx sdocs ###

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

@ -6,20 +6,22 @@ The `request` module lets you make simple yet powerful network requests.
<api name="Request">
@class
The `Request` object is used to make `GET`, `POST` or `PUT` network requests.
It is constructed with a URL to which the request is sent. Optionally the user
may specify a collection of headers and content to send alongside the request
and a callback which will be executed once the request completes.
The `Request` object is used to make `GET`, `POST`, `PUT`, or `HEAD`
network requests. It is constructed with a URL to which the request is sent.
Optionally the user may specify a collection of headers and content to send
alongside the request and a callback which will be executed once the
request completes.
Once a `Request` object has been created a `GET` request can be executed by
calling its `get()` method, a `POST` request by calling its `post()` method,
or a `PUT` request by calling its `put()` method.
a `PUT` request by calling its `put()` method, or a `HEAD` request by calling
its `head()` method.
When the server completes the request, the `Request` object emits a "complete"
event. Registered event listeners are passed a `Response` object.
Each `Request` object is designed to be used once. Once `GET`, `POST` or `PUT`
are called, attempting to call either will throw an error.
Each `Request` object is designed to be used once. Once `GET`, `POST`, `PUT`,
or `HEAD` are called, attempting to call either will throw an error.
Since the request is not being made by any particular website, requests made
here are not subject to the same-domain restriction that requests made in web
@ -150,6 +152,12 @@ Make a `PUT` request.
@returns {Request}
</api>
<api name="head">
@method
Make a `HEAD` request.
@returns {Request}
</api>
<api name="complete">
@event
The `Request` object emits this event when the request has completed and a
@ -165,8 +173,8 @@ Listener functions are passed the response to the request as a `Response` object
<api name="Response">
@class
The Response object contains the response to a network request issued using a
`Request` object. It is returned by the `get()`, `post()` or `put()` method of a
`Request` object.
`Request` object. It is returned by the `get()`, `post()`, `put()`, or `head()`
method of a `Request` object.
All members of a `Response` object are read-only.
<api name="text">

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

@ -11,11 +11,12 @@ module.metadata = {
const { Cc, Ci } = require('chrome');
const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader');
const { once } = require('../system/events');
const { exit, env, staticArgs, name } = require('../system');
const { exit, env, staticArgs } = require('../system');
const { when: unload } = require('../system/unload');
const { loadReason } = require('../self');
const { rootURI } = require("@loader/options");
const globals = require('../system/globals');
const xulApp = require('../system/xul-app');
const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
getService(Ci.nsIAppShellService);
@ -23,13 +24,20 @@ const NAME2TOPIC = {
'Firefox': 'sessionstore-windows-restored',
'Fennec': 'sessionstore-windows-restored',
'SeaMonkey': 'sessionstore-windows-restored',
'Thunderbird': 'mail-startup-done',
'*': 'final-ui-startup'
'Thunderbird': 'mail-startup-done'
};
// Set 'final-ui-startup' as default topic for unknown applications
let appStartup = 'final-ui-startup';
// Gets the topic that fit best as application startup event, in according with
// the current application (e.g. Firefox, Fennec, Thunderbird...)
const APP_STARTUP = NAME2TOPIC[name] || NAME2TOPIC['*'];
for (let name of Object.keys(NAME2TOPIC)) {
if (xulApp.is(name)) {
appStartup = NAME2TOPIC[name];
break;
}
}
// Initializes default preferences
function setDefaultPrefs(prefsURI) {
@ -66,7 +74,7 @@ function definePseudo(loader, id, exports) {
}
function wait(reason, options) {
once(APP_STARTUP, function() {
once(appStartup, function() {
startup(null, options);
});
}
@ -145,10 +153,10 @@ function run(options) {
program.main({
loadReason: loadReason,
staticArgs: staticArgs
}, {
staticArgs: staticArgs
}, {
print: function print(_) { dump(_ + '\n') },
quit: exit
quit: exit
});
}
} catch (error) {

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

@ -566,7 +566,7 @@ ModuleTabTracker.prototype = {
_TAB_EVENTS: ["TabOpen", "TabClose", "TabSelect", "DOMContentLoaded",
"load", "MozAfterPaint"],
_safeTrackTab: function safeTrackTab(tab) {
tab.addEventListener("load", this, false);
tab.linkedBrowser.addEventListener("load", this, true);
tab.linkedBrowser.addEventListener("MozAfterPaint", this, false);
this._tabs.push(tab);
try {
@ -576,7 +576,7 @@ ModuleTabTracker.prototype = {
}
},
_safeUntrackTab: function safeUntrackTab(tab) {
tab.removeEventListener("load", this, false);
tab.linkedBrowser.removeEventListener("load", this, true);
tab.linkedBrowser.removeEventListener("MozAfterPaint", this, false);
var index = this._tabs.indexOf(tab);
if (index == -1)
@ -617,7 +617,11 @@ ModuleTabTracker.prototype = {
}
},
_safeLoad: function safeLoad(event) {
let tab = event.target;
let win = event.currentTarget.ownerDocument.defaultView;
let tabIndex = win.gBrowser.getBrowserIndexForDocument(event.target);
if (tabIndex == -1)
return;
let tab = win.gBrowser.tabContainer.getItemAtIndex(tabIndex);
let index = this._tabs.indexOf(tab);
if (index == -1)
console.error("internal error: tab not found");

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

@ -11,6 +11,7 @@ const { Cc, Ci } = require('chrome');
const { EventEmitter } = require('../deprecated/events');
const { Trait } = require('../deprecated/traits');
const { when } = require('../system/unload');
const events = require('../system/events');
const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser,
getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils');
const errors = require('../deprecated/errors');
@ -68,6 +69,8 @@ function WindowTracker(delegate) {
for each (let window in getWindows())
this._regWindow(window);
windowWatcher.registerNotification(this);
this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this);
events.on('toplevel-window-ready', this._onToplevelWindowReady);
require('../system/unload').ensure(this);
@ -116,6 +119,7 @@ WindowTracker.prototype = {
unload: function unload() {
windowWatcher.unregisterNotification(this);
events.off('toplevel-window-ready', this._onToplevelWindowReady);
for each (let window in getWindows())
this._unregWindow(window);
},
@ -128,14 +132,20 @@ WindowTracker.prototype = {
}
}),
_onToplevelWindowReady: function _onToplevelWindowReady({subject}) {
let window = subject;
// ignore private windows if they are not supported
if (ignoreWindow(window))
return;
this._regWindow(window);
},
observe: errors.catchAndLog(function observe(subject, topic, data) {
var window = subject.QueryInterface(Ci.nsIDOMWindow);
// ignore private windows if they are not supported
if (ignoreWindow(window))
return;
if (topic == 'domwindowopened')
this._regWindow(window);
else
if (topic == 'domwindowclosed')
this._unregWindow(window);
})
};

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

@ -19,7 +19,8 @@ const { isPrivateBrowsingSupported } = require('./self');
const { isWindowPBSupported } = require('./private-browsing/utils');
const { Class } = require("./core/heritage");
const { merge } = require("./util/object");
const { WorkerHost, Worker, detach, attach } = require("./worker/utils");
const { WorkerHost, Worker, detach, attach,
requiresAddonGlobal } = require("./worker/utils");
const { Disposable } = require("./core/disposable");
const { contract: loaderContract } = require("./content/loader");
const { contract } = require("./util/contract");
@ -32,24 +33,6 @@ const { filter, pipe } = require("./event/utils");
const { getNodeView, getActiveView } = require("./view/core");
const { isNil, isObject } = require("./lang/type");
let isArray = Array.isArray;
let assetsURI = require("./self").data.url();
function isAddonContent({ contentURL }) {
return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0;
}
function hasContentScript({ contentScript, contentScriptFile }) {
return (isArray(contentScript) ? contentScript.length > 0 :
!!contentScript) ||
(isArray(contentScriptFile) ? contentScriptFile.length > 0 :
!!contentScriptFile);
}
function requiresAddonGlobal(model) {
return isAddonContent(model) && !hasContentScript(model);
}
function getAttachEventType(model) {
let when = model.contentScriptWhen;
return requiresAddonGlobal(model) ? "sdk-panel-content-changed" :

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

@ -50,12 +50,14 @@ const { validateOptions, validateSingleOption } = new OptionsValidator({
const REUSE_ERROR = "This request object has been used already. You must " +
"create a new one to make a new request."
// Utility function to prep the request since it's the same between GET and
// POST
// Utility function to prep the request since it's the same between
// request types
function runRequest(mode, target) {
let source = request(target)
let { xhr, url, content, contentType, headers, overrideMimeType } = source;
let isGetOrHead = (mode == "GET" || mode == "HEAD");
// If this request has already been used, then we can't reuse it.
// Throw an error.
if (xhr)
@ -63,11 +65,11 @@ function runRequest(mode, target) {
xhr = source.xhr = new XMLHttpRequest();
// Build the data to be set. For GET requests, we want to append that to
// the URL before opening the request.
// Build the data to be set. For GET or HEAD requests, we want to append that
// to the URL before opening the request.
let data = stringify(content);
// If the URL already has ? in it, then we want to just use &
if (mode == "GET" && data)
if (isGetOrHead && data)
url = url + (/\?/.test(url) ? "&" : "?") + data;
// open the request
@ -97,8 +99,8 @@ function runRequest(mode, target) {
};
// actually send the request.
// We don't want to send data on GET requests.
xhr.send(mode !== "GET" ? data : null);
// We don't want to send data on GET or HEAD requests.
xhr.send(!isGetOrHead ? data : null);
}
const Request = Class({
@ -138,6 +140,10 @@ const Request = Class({
put: function() {
runRequest('PUT', this);
return this;
},
head: function() {
runRequest('HEAD', this);
return this;
}
});
exports.Request = Request;

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

@ -12,6 +12,8 @@ const { Cc, Ci, Cr } = require("chrome");
const { Class } = require("./core/heritage");
const base64 = require("./base64");
var tlds = Cc["@mozilla.org/network/effective-tld-service;1"]
.getService(Ci.nsIEffectiveTLDService);
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
@ -19,6 +21,9 @@ var ios = Cc['@mozilla.org/network/io-service;1']
var resProt = ios.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"]
.getService(Ci.nsIURLParser);
function newURI(uriStr, base) {
try {
let baseURI = base ? ios.newURI(base, null, null) : null;
@ -92,11 +97,29 @@ function URL(url, base) {
port = uri.port == -1 ? null : uri.port;
} catch (e if e.result == Cr.NS_ERROR_FAILURE) {}
let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}];
URLParser.parsePath.apply(URLParser, uriData);
let [{ value: filepathPos }, { value: filepathLen },
{ value: queryPos }, { value: queryLen },
{ value: refPos }, { value: refLen }] = uriData.slice(2);
let hash = uri.ref ? "#" + uri.ref : "";
let pathname = uri.path.substr(filepathPos, filepathLen);
let search = uri.path.substr(queryPos, queryLen);
search = search ? "?" + search : "";
this.__defineGetter__("scheme", function() uri.scheme);
this.__defineGetter__("userPass", function() userPass);
this.__defineGetter__("host", function() host);
this.__defineGetter__("hostname", function() host);
this.__defineGetter__("port", function() port);
this.__defineGetter__("path", function() uri.path);
this.__defineGetter__("pathname", function() pathname);
this.__defineGetter__("hash", function() hash);
this.__defineGetter__("href", function() uri.spec);
this.__defineGetter__("origin", function() uri.prePath);
this.__defineGetter__("protocol", function() uri.scheme + ":");
this.__defineGetter__("search", function() search);
Object.defineProperties(this, {
toString: {
@ -235,6 +258,17 @@ const DataURL = Class({
exports.DataURL = DataURL;
let getTLD = exports.getTLD = function getTLD (url) {
let uri = newURI(url.toString());
let tld = null;
try {
tld = tlds.getPublicSuffix(uri);
} catch (e if
e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS ||
e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {}
return tld;
};
let isValidURI = exports.isValidURI = function (uri) {
try {
newURI(uri);

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

@ -17,10 +17,34 @@ const { Loader } = require("../content/loader");
const { merge } = require("../util/object");
const { emit } = require("../event/core");
const LegacyWorker = WorkerTrait.resolve({
let assetsURI = require("../self").data.url();
let isArray = Array.isArray;
function isAddonContent({ contentURL }) {
return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0;
}
function hasContentScript({ contentScript, contentScriptFile }) {
return (isArray(contentScript) ? contentScript.length > 0 :
!!contentScript) ||
(isArray(contentScriptFile) ? contentScriptFile.length > 0 :
!!contentScriptFile);
}
function requiresAddonGlobal(model) {
return isAddonContent(model) && !hasContentScript(model);
}
exports.requiresAddonGlobal = requiresAddonGlobal;
const LegacyWorker = WorkerTrait.compose(Loader).resolve({
_setListeners: "__setListeners",
}).compose(Loader, {
_injectInDocument: "__injectInDocument",
contentURL: "__contentURL"
}).compose({
_setListeners: function() {},
get contentURL() this._window.document.URL,
get _injectInDocument() requiresAddonGlobal(this),
attach: function(window) this._attach(window),
detach: function() this._workerCleanup()
});

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

@ -49,8 +49,8 @@ function open(url, options) {
let tab = getActiveTab(chromeWindow);
tab.addEventListener("load", function ready(event) {
let { document } = getTabContentWindow(this);
tab.linkedBrowser.addEventListener("load", function ready(event) {
let { document } = getTabContentWindow(tab);
if (document.readyState === "complete" && document.URL === url) {
this.removeEventListener(event.type, ready);
@ -60,7 +60,7 @@ function open(url, options) {
resolve(document.defaultView);
}
})
}, true);
setTabURL(tab, url);
});

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

@ -0,0 +1,3 @@
<script>
addon.postMessage("hello addon")
</script>

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

@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Panel } = require("sdk/panel")
const { data } = require("sdk/self")
exports["test addon global"] = function(assert, done) {
let panel = Panel({
contentURL: //"data:text/html,now?",
data.url("./index.html"),
onMessage: function(message) {
assert.pass("got message from panel script");
panel.destroy();
done();
},
onError: function(error) {
asser.fail(Error("failed to recieve message"));
done();
}
});
};
require("sdk/test/runner").runTestsFromModule(module);

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

@ -0,0 +1,3 @@
{
"id": "test-privileged-addon"
}

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

@ -218,6 +218,26 @@ exports.testInvalidJSON = function (test) {
});
}
exports.testHead = function (test) {
let srv = startServerAsync(port, basePath);
srv.registerPathHandler("/test-head",
function handle(request, response) {
response.setHeader("Content-Type", "text/plain", false);
});
test.waitUntilDone();
Request({
url: "http://localhost:" + port + "/test-head",
onComplete: function (response) {
test.assertEqual(response.text, "");
test.assertEqual(response.statusText, "OK");
test.assertEqual(response.headers["Content-Type"], "text/plain");
srv.stop(function() test.done());
}
}).head();
}
function runMultipleURLs (srv, test, options) {
let urls = [options.url, URL(options.url)];
let cb = options.onComplete;

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

@ -50,15 +50,15 @@ function open(url, options) {
let tab = getActiveTab(chromeWindow);
tab.addEventListener("load", function ready(event) {
let { document } = getTabContentWindow(this);
tab.linkedBrowser.addEventListener("load", function ready(event) {
let { document } = getTabContentWindow(tab);
if (document.readyState === "complete" && document.URL === url) {
this.removeEventListener(event.type, ready);
resolve(document.defaultView);
}
})
}, true);
setTabURL(tab, url);
});

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

@ -3,6 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var url = require("sdk/url");
var { Loader } = require("sdk/test/loader");
var { pathFor } = require("sdk/system");
var file = require("sdk/io/file");
var loader = Loader(module);
var httpd = loader.require("sdk/test/httpd");
var port = 8099;
var tabs = require("sdk/tabs");
exports.testResolve = function(test) {
test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(),
@ -33,12 +40,49 @@ exports.testResolve = function(test) {
};
exports.testParseHttp = function(test) {
var info = url.URL("http://foo.com/bar");
var aUrl = "http://sub.foo.com/bar?locale=en-US&otherArg=%20x%20#myhash";
var info = url.URL(aUrl);
test.assertEqual(info.scheme, "http");
test.assertEqual(info.host, "foo.com");
test.assertEqual(info.protocol, "http:");
test.assertEqual(info.host, "sub.foo.com");
test.assertEqual(info.hostname, "sub.foo.com");
test.assertEqual(info.port, null);
test.assertEqual(info.userPass, null);
test.assertEqual(info.path, "/bar");
test.assertEqual(info.path, "/bar?locale=en-US&otherArg=%20x%20#myhash");
test.assertEqual(info.pathname, "/bar");
test.assertEqual(info.href, aUrl);
test.assertEqual(info.hash, "#myhash");
test.assertEqual(info.search, "?locale=en-US&otherArg=%20x%20");
};
exports.testParseHttpSearchAndHash = function (test) {
var info = url.URL("https://www.moz.com/some/page.html");
test.assertEqual(info.hash, "");
test.assertEqual(info.search, "");
var hashOnly = url.URL("https://www.sub.moz.com/page.html#justhash");
test.assertEqual(hashOnly.search, "");
test.assertEqual(hashOnly.hash, "#justhash");
var queryOnly = url.URL("https://www.sub.moz.com/page.html?my=query");
test.assertEqual(queryOnly.search, "?my=query");
test.assertEqual(queryOnly.hash, "");
var qMark = url.URL("http://www.moz.org?");
test.assertEqual(qMark.search, "");
test.assertEqual(qMark.hash, "");
var hash = url.URL("http://www.moz.org#");
test.assertEqual(hash.search, "");
test.assertEqual(hash.hash, "");
var empty = url.URL("http://www.moz.org?#");
test.assertEqual(hash.search, "");
test.assertEqual(hash.hash, "");
var strange = url.URL("http://moz.org?test1#test2?test3");
test.assertEqual(strange.search, "?test1");
test.assertEqual(strange.hash, "#test2?test3");
};
exports.testParseHttpWithPort = function(test) {
@ -163,10 +207,12 @@ exports.testStringInterface = function(test) {
var a = URL(EM);
// make sure the standard URL properties are enumerable and not the String interface bits
test.assertEqual(Object.keys(a), "scheme,userPass,host,port,path", "enumerable key list check for URL.");
test.assertEqual(Object.keys(a),
"scheme,userPass,host,hostname,port,path,pathname,hash,href,origin,protocol,search",
"enumerable key list check for URL.");
test.assertEqual(
JSON.stringify(a),
"{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"port\":null,\"path\":\"addons\"}",
"{\"scheme\":\"about\",\"userPass\":null,\"host\":null,\"hostname\":null,\"port\":null,\"path\":\"addons\",\"pathname\":\"addons\",\"hash\":\"\",\"href\":\"about:addons\",\"origin\":\"about:\",\"protocol\":\"about:\",\"search\":\"\"}",
"JSON.stringify should return a object with correct props and vals.");
// make sure that the String interface exists and works as expected
@ -261,6 +307,53 @@ exports.testURLFromURL = function(test) {
test.assertEqual(aURL.toString(), bURL.toString(), 'Making a URL from a URL works');
};
exports.testTLD = function(test) {
let urls = [
{ url: 'http://my.sub.domains.mozilla.co.uk', tld: 'co.uk' },
{ url: 'http://my.mozilla.com', tld: 'com' },
{ url: 'http://my.domains.mozilla.org.hk', tld: 'org.hk' },
{ url: 'chrome://global/content/blah', tld: 'global' },
{ url: 'data:text/plain;base64,QXdlc29tZSE=', tld: null },
{ url: 'https://1.2.3.4', tld: null }
];
urls.forEach(function (uri) {
test.assertEqual(url.getTLD(uri.url), uri.tld);
test.assertEqual(url.getTLD(url.URL(uri.url)), uri.tld);
});
}
exports.testWindowLocationMatch = function (test) {
let srv = serve();
let aUrl = 'http://localhost:' + port + '/index.html?q=aQuery#somehash';
let urlObject = url.URL(aUrl);
test.waitUntilDone();
tabs.open({
url: aUrl,
onReady: function (tab) {
tab.attach({
onMessage: function (loc) {
for (let prop in loc) {
test.assertEqual(urlObject[prop], loc[prop], prop + ' matches');
}
tab.close();
srv.stop(test.done.bind(test));
},
contentScript: '(' + function () {
let res = {};
// `origin` is `null` in this context???
let props = 'hostname,port,pathname,hash,href,protocol,search'.split(',');
props.forEach(function (prop) {
res[prop] = window.location[prop];
});
self.postMessage(res);
} + ')()'
});
}
})
};
function validURIs() {
return [
'http://foo.com/blah_blah',
@ -352,3 +445,16 @@ function invalidURIs () {
// 'http://10.1.1.254'
];
}
function serve () {
let basePath = pathFor("ProfD");
let filePath = file.join(basePath, 'index.html');
let content = "<html><head></head><body><h1>url tests</h1></body></html>";
let fileStream = file.open(filePath, 'w');
fileStream.write(content);
fileStream.close();
let srv = httpd.startServerAsync(port, basePath);
return srv;
}

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

@ -28,15 +28,13 @@ var tests = {
}
function triggerIconPanel() {
let statusIcon = document.getElementById("social-provider-button").nextSibling;
info("status icon is " + statusIcon);
waitForCondition(function() {
statusIcon = document.getElementById("social-provider-button").nextSibling;
info("status icon is " + statusIcon);
return !!statusIcon;
let button = document.getElementById("social-toolbar-item");
// by default, button has two children. wait for a 3rd to be added
return button.childNodes.length > 2;
}, function() {
// Click the button to trigger its contentPanel
let panel = document.getElementById("social-notification-panel");
let statusIcon = document.getElementById("social-provider-button").nextSibling;
EventUtils.synthesizeMouseAtCenter(statusIcon, {});
}, "Status icon didn't become non-hidden");
}

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

@ -22,6 +22,7 @@
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StandardInteger.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/indexedDB/FileInfo.h"
@ -343,6 +344,10 @@ protected:
bool mStoredFile;
};
/**
* This class may be used off the main thread, and in particular, its
* constructor and destructor may not run on the same thread. Be careful!
*/
class nsDOMMemoryFile : public nsDOMFile
{
public:
@ -394,6 +399,8 @@ protected:
: mData(aMemoryBuffer)
, mLength(aLength)
{
mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
if (!sDataOwners) {
sDataOwners = new mozilla::LinkedList<DataOwner>();
EnsureMemoryReporterRegistered();
@ -402,6 +409,8 @@ protected:
}
~DataOwner() {
mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
remove();
if (sDataOwners->isEmpty()) {
// Free the linked list if it's empty.
@ -413,8 +422,13 @@ protected:
static void EnsureMemoryReporterRegistered();
static bool sMemoryReporterRegistered;
// sDataOwners and sMemoryReporterRegistered may only be accessed while
// holding sDataOwnerMutex! You also must hold the mutex while touching
// elements of the linked list that DataOwner inherits from.
static mozilla::StaticMutex sDataOwnerMutex;
static mozilla::StaticAutoPtr<mozilla::LinkedList<DataOwner> > sDataOwners;
static bool sMemoryReporterRegistered;
void* mData;
uint64_t mLength;
};

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

@ -82,26 +82,26 @@ public:
ePercent = 0x0F, // 1111
// Values below here won't matter, they'll be always stored in the 'misc'
// struct.
eCSSStyleRule = 0x10
,eURL = 0x11
,eImage = 0x12
,eAtomArray = 0x13
,eDoubleValue = 0x14
,eIntMarginValue = 0x15
,eSVGTypesBegin = 0x16
,eSVGAngle = eSVGTypesBegin
,eSVGIntegerPair = 0x17
,eSVGLength = 0x18
,eSVGLengthList = 0x19
,eSVGNumberList = 0x20
,eSVGNumberPair = 0x21
,eSVGPathData = 0x22
,eSVGPointList = 0x23
,eSVGPreserveAspectRatio = 0x24
,eSVGStringList = 0x25
,eSVGTransformList = 0x26
,eSVGViewBox = 0x27
,eSVGTypesEnd = 0x34
eCSSStyleRule = 0x10
,eURL = 0x11
,eImage = 0x12
,eAtomArray = 0x13
,eDoubleValue = 0x14
,eIntMarginValue = 0x15
,eSVGAngle = 0x16
,eSVGTypesBegin = eSVGAngle
,eSVGIntegerPair = 0x17
,eSVGLength = 0x18
,eSVGLengthList = 0x19
,eSVGNumberList = 0x1A
,eSVGNumberPair = 0x1B
,eSVGPathData = 0x1C
,eSVGPointList = 0x1D
,eSVGPreserveAspectRatio = 0x1E
,eSVGStringList = 0x1F
,eSVGTransformList = 0x20
,eSVGViewBox = 0x21
,eSVGTypesEnd = eSVGViewBox
};
nsAttrValue();

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

@ -611,6 +611,9 @@ nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream)
return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream);
}
/* static */ StaticMutex
nsDOMMemoryFile::DataOwner::sDataOwnerMutex;
/* static */ StaticAutoPtr<LinkedList<nsDOMMemoryFile::DataOwner> >
nsDOMMemoryFile::DataOwner::sDataOwners;
@ -635,6 +638,8 @@ class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
{
typedef nsDOMMemoryFile::DataOwner DataOwner;
StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
if (!DataOwner::sDataOwners) {
return NS_OK;
}

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

@ -4828,7 +4828,7 @@ nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
if (rv.Failed()) {
return nullptr;
}
return content.forget().get()->AsElement();
return dont_AddRef(content.forget().get()->AsElement());
}
NS_IMETHODIMP
@ -4865,7 +4865,7 @@ nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
if (rv.Failed()) {
return nullptr;
}
return content.forget().get()->AsElement();
return dont_AddRef(content.forget().get()->AsElement());
}
NS_IMETHODIMP
@ -6975,7 +6975,7 @@ nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
nsEventDispatcher::CreateEvent(const_cast<nsIDocument*>(this),
presContext, nullptr, aEventType,
getter_AddRefs(ev));
return ev ? ev.forget().get()->InternalDOMEvent() : nullptr;
return ev ? dont_AddRef(ev.forget().get()->InternalDOMEvent()) : nullptr;
}
void

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

@ -586,7 +586,13 @@ nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext,
MOZ_ASSERT(scriptListener,
"How can we have an event handler with no nsIJSEventListener?");
bool same = scriptListener->GetHandler() == aHandler;
// Possibly the same listener, but update still the context and scope.
scriptListener->SetHandler(aHandler, aContext, aScopeObject);
if (mTarget && !same) {
mTarget->EventListenerRemoved(aName);
mTarget->EventListenerAdded(aName);
}
}
if (NS_SUCCEEDED(rv) && ls) {

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

@ -90,7 +90,6 @@ private:
nsIContent* nsIMEStateManager::sContent = nullptr;
nsPresContext* nsIMEStateManager::sPresContext = nullptr;
bool nsIMEStateManager::sInstalledMenuKeyboardListener = false;
bool nsIMEStateManager::sInSecureInputMode = false;
bool nsIMEStateManager::sIsTestingIME = false;
nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
@ -243,29 +242,6 @@ nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
return NS_OK;
}
// Handle secure input mode for password field input.
bool contentIsPassword = false;
if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML) {
if (aContent->Tag() == nsGkAtoms::input) {
nsAutoString type;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
contentIsPassword = type.LowerCaseEqualsLiteral("password");
}
}
if (sInSecureInputMode) {
if (!contentIsPassword) {
if (NS_SUCCEEDED(widget->EndSecureKeyboardInput())) {
sInSecureInputMode = false;
}
}
} else {
if (contentIsPassword) {
if (NS_SUCCEEDED(widget->BeginSecureKeyboardInput())) {
sInSecureInputMode = true;
}
}
}
IMEState newState = GetNewIMEState(aPresContext, aContent);
if (!focusActuallyChanging) {
// actual focus isn't changing, but if IME enabled state is changing,

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

@ -128,7 +128,6 @@ protected:
static nsIContent* sContent;
static nsPresContext* sPresContext;
static bool sInstalledMenuKeyboardListener;
static bool sInSecureInputMode;
static bool sIsTestingIME;
static nsTextStateManager* sTextStateObserver;

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

@ -321,8 +321,8 @@ nsGenericHTMLElement::Dataset()
slots->mDataset = new nsDOMStringMap(this);
}
NS_ADDREF(slots->mDataset);
return slots->mDataset;
nsRefPtr<nsDOMStringMap> ret = slots->mDataset;
return ret.forget();
}
nsresult
@ -3174,7 +3174,8 @@ nsGenericHTMLElement::SetItemValue(JSContext* aCx, JS::Value aValue,
}
FakeDependentString string;
if (!ConvertJSValueToString(aCx, aValue, &aValue, eStringify, eStringify, string)) {
JS::Rooted<JS::Value> value(aCx, aValue);
if (!ConvertJSValueToString(aCx, value, &value, eStringify, eStringify, string)) {
aError.Throw(NS_ERROR_UNEXPECTED);
return;
}

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

@ -393,6 +393,10 @@ public:
static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCurveLength, double duration, double t)
{
if (t >= startTime + duration) {
// After the duration, return the last curve value
return aCurve[aCurveLength - 1];
}
double ratio = (t - startTime) / duration;
MOZ_ASSERT(ratio >= 0.0, "Ratio can never be negative here");
if (ratio >= 1.0) {

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

@ -7,13 +7,13 @@
#define MOZILLA_AUDIONODEENGINE_H_
#include "AudioSegment.h"
#include "mozilla/dom/AudioNode.h"
#include "mozilla/dom/AudioParam.h"
#include "mozilla/Mutex.h"
namespace mozilla {
namespace dom {
class AudioNode;
struct ThreeDPoint;
}
@ -154,6 +154,7 @@ public:
, mInputCount(aNode ? aNode->NumberOfInputs() : 1)
, mOutputCount(aNode ? aNode->NumberOfOutputs() : 0)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(AudioNodeEngine);
}
virtual ~AudioNodeEngine()
@ -175,7 +176,8 @@ public:
NS_ERROR("Invalid SetInt32Parameter index");
}
virtual void SetTimelineParameter(uint32_t aIndex,
const dom::AudioParamTimeline& aValue)
const dom::AudioParamTimeline& aValue,
TrackRate aSampleRate)
{
NS_ERROR("Invalid SetTimelineParameter index");
}
@ -198,8 +200,8 @@ public:
* Produce the next block of audio samples, given input samples aInput
* (the mixed data for input 0).
* aInput is guaranteed to have float sample format (if it has samples at all)
* and to have been resampled to IdealAudioRate(), and to have exactly
* WEBAUDIO_BLOCK_SIZE samples.
* and to have been resampled to the sampling rate for the stream, and to have
* exactly WEBAUDIO_BLOCK_SIZE samples.
* *aFinished is set to false by the caller. If the callee sets it to true,
* we'll finish the stream and not call this again.
*/

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

@ -15,7 +15,9 @@ namespace mozilla {
/**
* An AudioNodeStream produces a single audio track with ID
* AUDIO_NODE_STREAM_TRACK_ID. This track has rate IdealAudioRate().
* AUDIO_NODE_STREAM_TRACK_ID. This track has rate AudioContext::sIdealAudioRate
* for regular audio contexts, and the rate requested by the web content
* for offline audio contexts.
* Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
*/
static const int AUDIO_NODE_STREAM_TRACK_ID = 1;
@ -107,13 +109,17 @@ AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
public:
Message(AudioNodeStream* aStream, uint32_t aIndex,
const AudioParamTimeline& aValue)
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
: ControlMessage(aStream),
mValue(aValue),
mSampleRate(aStream->SampleRate()),
mIndex(aIndex) {}
virtual void Run()
{
static_cast<AudioNodeStream*>(mStream)->Engine()->
SetTimelineParameter(mIndex, mValue);
SetTimelineParameter(mIndex, mValue, mSampleRate);
}
AudioParamTimeline mValue;
TrackRate mSampleRate;
uint32_t mIndex;
};
GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
@ -237,11 +243,11 @@ AudioNodeStream::EnsureTrack()
nsAutoPtr<MediaSegment> segment(new AudioSegment());
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, IdealAudioRate(), 0,
l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID, mSampleRate, 0,
MediaStreamListener::TRACK_EVENT_CREATED,
*segment);
}
track = &mBuffer.AddTrack(AUDIO_NODE_STREAM_TRACK_ID, IdealAudioRate(), 0, segment.forget());
track = &mBuffer.AddTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate, 0, segment.forget());
}
return track;
}
@ -437,7 +443,7 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
AudioSegment tmpSegment;
tmpSegment.AppendAndConsumeChunk(&copyChunk);
l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID,
IdealAudioRate(), segment->GetDuration(), 0,
mSampleRate, segment->GetDuration(), 0,
tmpSegment);
}
}
@ -463,7 +469,7 @@ AudioNodeStream::FinishOutput()
MediaStreamListener* l = mListeners[j];
AudioSegment emptySegment;
l->NotifyQueuedTrackChanges(Graph(), AUDIO_NODE_STREAM_TRACK_ID,
IdealAudioRate(),
mSampleRate,
track->GetSegment()->GetDuration(),
MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
}

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

@ -47,14 +47,17 @@ public:
* Transfers ownership of aEngine to the new AudioNodeStream.
*/
AudioNodeStream(AudioNodeEngine* aEngine,
MediaStreamGraph::AudioNodeStreamKind aKind)
MediaStreamGraph::AudioNodeStreamKind aKind,
TrackRate aSampleRate)
: ProcessedMediaStream(nullptr),
mEngine(aEngine),
mSampleRate(aSampleRate),
mKind(aKind),
mNumberOfInputChannels(2),
mMarkAsFinishedAfterThisBlock(false),
mAudioParamStream(false)
{
MOZ_ASSERT(NS_IsMainThread());
mChannelCountMode = dom::ChannelCountMode::Max;
mChannelInterpretation = dom::ChannelInterpretation::Speakers;
// AudioNodes are always producing data
@ -108,6 +111,7 @@ public:
// Any thread
AudioNodeEngine* Engine() { return mEngine; }
TrackRate SampleRate() const { return mSampleRate; }
protected:
void FinishOutput();
@ -119,6 +123,8 @@ protected:
nsAutoPtr<AudioNodeEngine> mEngine;
// The last block produced by this node.
OutputChunks mLastChunks;
// The stream's sampling rate
const TrackRate mSampleRate;
// Whether this is an internal or external stream
MediaStreamGraph::AudioNodeStreamKind mKind;
// The number of input channels that this stream requires. 0 means don't care.

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

@ -909,28 +909,28 @@ MediaStreamGraphImpl::EnsureNextIterationLocked(MonitorAutoLock& aLock)
}
static GraphTime
RoundUpToAudioBlock(GraphTime aTime)
RoundUpToAudioBlock(TrackRate aSampleRate, GraphTime aTime)
{
TrackRate rate = IdealAudioRate();
int64_t ticksAtIdealRate = (aTime*rate) >> MEDIA_TIME_FRAC_BITS;
int64_t ticksAtIdealaSampleRate = (aTime*aSampleRate) >> MEDIA_TIME_FRAC_BITS;
// Round up to nearest block boundary
int64_t blocksAtIdealRate =
(ticksAtIdealRate + (WEBAUDIO_BLOCK_SIZE - 1)) >>
int64_t blocksAtIdealaSampleRate =
(ticksAtIdealaSampleRate + (WEBAUDIO_BLOCK_SIZE - 1)) >>
WEBAUDIO_BLOCK_SIZE_BITS;
// Round up to nearest MediaTime unit
return
((((blocksAtIdealRate + 1)*WEBAUDIO_BLOCK_SIZE) << MEDIA_TIME_FRAC_BITS)
+ rate - 1)/rate;
((((blocksAtIdealaSampleRate + 1)*WEBAUDIO_BLOCK_SIZE) << MEDIA_TIME_FRAC_BITS)
+ aSampleRate - 1)/aSampleRate;
}
void
MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
TrackRate aSampleRate,
GraphTime aFrom,
GraphTime aTo)
{
GraphTime t = aFrom;
while (t < aTo) {
GraphTime next = RoundUpToAudioBlock(t + 1);
GraphTime next = RoundUpToAudioBlock(aSampleRate, t + 1);
for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
nsRefPtr<ProcessedMediaStream> ps = mStreams[i]->AsProcessedStream();
if (ps) {
@ -957,8 +957,6 @@ MediaStreamGraphImpl::RunThread()
if (!mRealtime) {
NS_ASSERTION(!mNonRealtimeIsRunning,
"We should not be running in non-realtime mode already");
NS_ASSERTION(mNonRealtimeTicksToProcess,
"We should have a non-zero number of ticks to process");
mNonRealtimeIsRunning = true;
}
@ -982,8 +980,21 @@ MediaStreamGraphImpl::RunThread()
UpdateStreamOrder();
// Find the sampling rate that we need to use for non-realtime graphs.
TrackRate sampleRate = IdealAudioRate();
if (!mRealtime) {
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
if (n) {
// We know that the rest of the streams will run at the same rate.
sampleRate = n->SampleRate();
break;
}
}
}
GraphTime endBlockingDecisions =
RoundUpToAudioBlock(mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
RoundUpToAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
bool ensureNextIteration = false;
// Grab pending stream input.
@ -1012,9 +1023,20 @@ MediaStreamGraphImpl::RunThread()
if (ps) {
AudioNodeStream* n = stream->AsAudioNodeStream();
if (n) {
#ifdef DEBUG
// Verify that the sampling rate for all of the following streams is the same
for (uint32_t j = i + 1; j < mStreams.Length(); ++j) {
AudioNodeStream* nextStream = mStreams[j]->AsAudioNodeStream();
if (nextStream) {
MOZ_ASSERT(n->SampleRate() == nextStream->SampleRate(),
"All AudioNodeStreams in the graph must have the same sampling rate");
}
}
#endif
// Since an AudioNodeStream is present, go ahead and
// produce audio block by block for all the rest of the streams.
ProduceDataForStreamsBlockByBlock(i, prevComputedTime, mStateComputedTime);
ProduceDataForStreamsBlockByBlock(i, n->SampleRate(), prevComputedTime, mStateComputedTime);
ticksProcessed += TimeToTicksRoundDown(n->SampleRate(), mStateComputedTime - prevComputedTime);
doneAllProducing = true;
} else {
ps->ProduceOutput(prevComputedTime, mStateComputedTime);
@ -1042,9 +1064,8 @@ MediaStreamGraphImpl::RunThread()
}
}
if (!mRealtime) {
ticksProcessed += TimeToTicksRoundDown(IdealAudioRate(), mStateComputedTime - prevComputedTime);
// Terminate processing if we've produce enough non-realtime ticks.
if (ticksProcessed >= mNonRealtimeTicksToProcess) {
if (!mForceShutDown && ticksProcessed >= mNonRealtimeTicksToProcess) {
// Wait indefinitely when we've processed enough non-realtime ticks.
// We'll be woken up when the graph shuts down.
MonitorAutoLock lock(mMonitor);
@ -1885,9 +1906,6 @@ MediaInputPort::Destroy()
{
Run();
}
// This does not need to be strongly referenced; the graph is holding
// a strong reference to the port, which we will remove. This will be the
// last message for the port.
MediaInputPort* mPort;
};
GraphImpl()->AppendMessage(new Message(this));
@ -1929,6 +1947,10 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags,
// The graph holds its reference implicitly
mPort.forget();
}
virtual void RunDuringShutdown()
{
Run();
}
nsRefPtr<MediaInputPort> mPort;
};
nsRefPtr<MediaInputPort> port = new MediaInputPort(aStream, this, aFlags,
@ -2064,6 +2086,10 @@ MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
MOZ_ASSERT(aGraph != gGraph, "Should not destroy the global graph here");
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
if (!graph->mNonRealtimeProcessing) {
// Start the graph, but don't produce anything
graph->StartNonRealtimeProcessing(0);
}
graph->ForceShutDown();
}
@ -2091,10 +2117,14 @@ MediaStreamGraph::CreateTrackUnionStream(DOMMediaStream* aWrapper)
AudioNodeStream*
MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
AudioNodeStreamKind aKind)
AudioNodeStreamKind aKind,
TrackRate aSampleRate)
{
MOZ_ASSERT(NS_IsMainThread());
AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind);
if (!aSampleRate) {
aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
}
AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate);
NS_ADDREF(stream);
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
stream->SetGraphImpl(graph);

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

@ -929,10 +929,13 @@ public:
enum AudioNodeStreamKind { INTERNAL_STREAM, EXTERNAL_STREAM };
/**
* Create a stream that will process audio for an AudioNode.
* Takes ownership of aEngine.
* Takes ownership of aEngine. aSampleRate is the sampling rate used
* for the stream. If 0 is passed, the sampling rate of the engine's
* node will get used.
*/
AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
AudioNodeStreamKind aKind);
AudioNodeStreamKind aKind,
TrackRate aSampleRate = 0);
/**
* Returns the number of graph updates sent. This can be used to track
* whether a given update has been processed by the graph thread and reflected

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

@ -264,6 +264,7 @@ public:
* This is called whenever we have an AudioNodeStream in the graph.
*/
void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
TrackRate aSampleRate,
GraphTime aFrom,
GraphTime aTo);
/**

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

@ -0,0 +1,12 @@
<script>
try { o1 = new AudioContext(); } catch(e) { }
try { o6 = o1.createGain(); } catch(e) { }
try { o8 = o1.createBufferSource(); } catch(e) { }
try { o6.gain.setValueCurveAtTime(Float32Array(7), 0, 0) } catch(e) { }
try { o8.connect(o6, 0, 0) } catch(e) { }
try { o8.buffer = function() {
o19 = o1.createBuffer(1, 1, 76309);
for(var i=0; i<1; ++i) { }
return o19;
}(); } catch(e) { }
</script>

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

@ -0,0 +1,3 @@
<script>
new OfflineAudioContext(1, 10, 48000);
</script>

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

@ -20,3 +20,5 @@ load 874915.html
load 874934.html
load 874952.html
load 875144.html
load 875596.html
load 875911.html

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

@ -54,7 +54,7 @@ public:
mResampler(nullptr),
mOffset(0), mDuration(0),
mLoopStart(0), mLoopEnd(0),
mSampleRate(0), mPosition(0), mChannels(0), mPlaybackRate(1.0f),
mBufferSampleRate(0), mPosition(0), mChannels(0), mPlaybackRate(1.0f),
mDopplerShift(1.0f),
mPlaybackRateTimeline(1.0f), mLoop(false)
{}
@ -66,7 +66,9 @@ public:
}
}
virtual void SetTimelineParameter(uint32_t aIndex, const dom::AudioParamTimeline& aValue)
virtual void SetTimelineParameter(uint32_t aIndex,
const dom::AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
switch (aIndex) {
case AudioBufferSourceNode::PLAYBACKRATE:
@ -76,7 +78,7 @@ public:
// resampler, we can release it.
if (mResampler && mPlaybackRateTimeline.HasSimpleValue() &&
mPlaybackRateTimeline.GetValue() == 1.0 &&
mSampleRate == IdealAudioRate()) {
mBufferSampleRate == aSampleRate) {
speex_resampler_destroy(mResampler);
mResampler = nullptr;
}
@ -111,7 +113,7 @@ public:
virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
{
switch (aIndex) {
case AudioBufferSourceNode::SAMPLE_RATE: mSampleRate = aParam; break;
case AudioBufferSourceNode::SAMPLE_RATE: mBufferSampleRate = aParam; break;
case AudioBufferSourceNode::OFFSET: mOffset = aParam; break;
case AudioBufferSourceNode::DURATION: mDuration = aParam; break;
case AudioBufferSourceNode::LOOP: mLoop = !!aParam; break;
@ -126,7 +128,7 @@ public:
mBuffer = aBuffer;
}
SpeexResamplerState* Resampler(uint32_t aChannels)
SpeexResamplerState* Resampler(AudioNodeStream* aStream, uint32_t aChannels)
{
if (aChannels != mChannels && mResampler) {
speex_resampler_destroy(mResampler);
@ -135,8 +137,8 @@ public:
if (!mResampler) {
mChannels = aChannels;
mResampler = speex_resampler_init(mChannels, mSampleRate,
ComputeFinalOutSampleRate(),
mResampler = speex_resampler_init(mChannels, mBufferSampleRate,
ComputeFinalOutSampleRate(aStream->SampleRate()),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
nullptr);
}
@ -174,19 +176,21 @@ public:
}
}
// Resamples input data to an output buffer, according to |mSampleRate| and
// Resamples input data to an output buffer, according to |mBufferSampleRate| and
// the playbackRate.
// The number of frames consumed/produced depends on the amount of space
// remaining in both the input and output buffer, and the playback rate (that
// is, the ratio between the output samplerate and the input samplerate).
void CopyFromInputBufferWithResampling(AudioChunk* aOutput,
void CopyFromInputBufferWithResampling(AudioNodeStream* aStream,
AudioChunk* aOutput,
uint32_t aChannels,
uintptr_t aSourceOffset,
uintptr_t aBufferOffset,
uint32_t aAvailableInInputBuffer,
uint32_t& aFramesRead,
uint32_t& aFramesWritten) {
double finalPlaybackRate = static_cast<double>(mSampleRate) / ComputeFinalOutSampleRate();
double finalPlaybackRate =
static_cast<double>(mBufferSampleRate) / ComputeFinalOutSampleRate(aStream->SampleRate());
uint32_t availableInOuputBuffer = WEBAUDIO_BLOCK_SIZE - aBufferOffset;
uint32_t inputSamples, outputSamples;
@ -199,7 +203,7 @@ public:
outputSamples = availableInOuputBuffer;
}
SpeexResamplerState* resampler = Resampler(aChannels);
SpeexResamplerState* resampler = Resampler(aStream, aChannels);
for (uint32_t i = 0; i < aChannels; ++i) {
uint32_t inSamples = inputSamples;
@ -256,7 +260,8 @@ public:
* This function knows when it needs to allocate the output buffer, and also
* optimizes the case where it can avoid memory allocations.
*/
void CopyFromBuffer(AudioChunk* aOutput,
void CopyFromBuffer(AudioNodeStream* aStream,
AudioChunk* aOutput,
uint32_t aChannels,
uint32_t* aOffsetWithinBlock,
TrackTicks* aCurrentPosition,
@ -266,7 +271,7 @@ public:
uint32_t numFrames = std::min(std::min(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
aBufferMax - aBufferOffset),
uint32_t(mStop - *aCurrentPosition));
if (numFrames == WEBAUDIO_BLOCK_SIZE && !ShouldResample()) {
if (numFrames == WEBAUDIO_BLOCK_SIZE && !ShouldResample(aStream->SampleRate())) {
BorrowFromInputBuffer(aOutput, aChannels, aBufferOffset);
*aOffsetWithinBlock += numFrames;
*aCurrentPosition += numFrames;
@ -276,7 +281,7 @@ public:
MOZ_ASSERT(*aOffsetWithinBlock == 0);
AllocateAudioBlock(aChannels, aOutput);
}
if (!ShouldResample()) {
if (!ShouldResample(aStream->SampleRate())) {
CopyFromInputBuffer(aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, numFrames);
*aOffsetWithinBlock += numFrames;
*aCurrentPosition += numFrames;
@ -286,7 +291,7 @@ public:
availableInInputBuffer = aBufferMax - aBufferOffset;
CopyFromInputBufferWithResampling(aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, availableInInputBuffer, framesRead, framesWritten);
CopyFromInputBufferWithResampling(aStream, aOutput, aChannels, aBufferOffset, *aOffsetWithinBlock, availableInInputBuffer, framesRead, framesWritten);
*aOffsetWithinBlock += framesWritten;
*aCurrentPosition += framesRead;
mPosition += framesRead;
@ -302,7 +307,7 @@ public:
return mStart + mPosition;
}
uint32_t ComputeFinalOutSampleRate()
uint32_t ComputeFinalOutSampleRate(TrackRate aStreamSampleRate)
{
if (mPlaybackRate <= 0 || mPlaybackRate != mPlaybackRate) {
mPlaybackRate = 1.0f;
@ -310,15 +315,15 @@ public:
if (mDopplerShift <= 0 || mDopplerShift != mDopplerShift) {
mDopplerShift = 1.0f;
}
return WebAudioUtils::TruncateFloatToInt<uint32_t>(IdealAudioRate() /
return WebAudioUtils::TruncateFloatToInt<uint32_t>(aStreamSampleRate /
(mPlaybackRate * mDopplerShift));
}
bool ShouldResample() const
bool ShouldResample(TrackRate aStreamSampleRate) const
{
return !(mPlaybackRate == 1.0 &&
mDopplerShift == 1.0 &&
mSampleRate == IdealAudioRate());
mBufferSampleRate == aStreamSampleRate);
}
void UpdateSampleRateIfNeeded(AudioNodeStream* aStream, uint32_t aChannels)
@ -331,16 +336,16 @@ public:
// Make sure the playback rate and the doppler shift are something
// our resampler can work with.
if (ComputeFinalOutSampleRate() == 0) {
if (ComputeFinalOutSampleRate(aStream->SampleRate()) == 0) {
mPlaybackRate = 1.0;
mDopplerShift = 1.0;
}
uint32_t currentOutSampleRate, currentInSampleRate;
if (ShouldResample()) {
SpeexResamplerState* resampler = Resampler(aChannels);
if (ShouldResample(aStream->SampleRate())) {
SpeexResamplerState* resampler = Resampler(aStream, aChannels);
speex_resampler_get_rate(resampler, &currentInSampleRate, &currentOutSampleRate);
uint32_t finalSampleRate = ComputeFinalOutSampleRate();
uint32_t finalSampleRate = ComputeFinalOutSampleRate(aStream->SampleRate());
if (currentOutSampleRate != finalSampleRate) {
speex_resampler_set_rate(resampler, currentInSampleRate, finalSampleRate);
speex_resampler_skip_zeros(mResampler);
@ -382,14 +387,14 @@ public:
TrackTicks t = currentPosition - mStart;
if (mLoop) {
if (mOffset + t < mLoopEnd) {
CopyFromBuffer(aOutput, channels, &written, &currentPosition, mOffset + t, mLoopEnd);
CopyFromBuffer(aStream, aOutput, channels, &written, &currentPosition, mOffset + t, mLoopEnd);
} else {
uint32_t offsetInLoop = (mOffset + t - mLoopEnd) % (mLoopEnd - mLoopStart);
CopyFromBuffer(aOutput, channels, &written, &currentPosition, mLoopStart + offsetInLoop, mLoopEnd);
CopyFromBuffer(aStream, aOutput, channels, &written, &currentPosition, mLoopStart + offsetInLoop, mLoopEnd);
}
} else {
if (mOffset + t < mDuration) {
CopyFromBuffer(aOutput, channels, &written, &currentPosition, mOffset + t, mDuration);
CopyFromBuffer(aStream, aOutput, channels, &written, &currentPosition, mOffset + t, mDuration);
} else {
FillWithZeroes(aOutput, channels, &written, &currentPosition, TRACK_TICKS_MAX);
}
@ -427,7 +432,7 @@ public:
int32_t mDuration;
int32_t mLoopStart;
int32_t mLoopEnd;
int32_t mSampleRate;
int32_t mBufferSampleRate;
uint32_t mPosition;
uint32_t mChannels;
float mPlaybackRate;

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

@ -51,7 +51,8 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow,
uint32_t aNumberOfChannels,
uint32_t aLength,
float aSampleRate)
: mDestination(new AudioDestinationNode(this, aIsOffline,
: mSampleRate(aIsOffline ? aSampleRate : IdealAudioRate())
, mDestination(new AudioDestinationNode(this, aIsOffline,
aNumberOfChannels,
aLength, aSampleRate))
, mIsOffline(aIsOffline)
@ -107,8 +108,8 @@ AudioContext::Constructor(const GlobalObject& aGlobal,
return nullptr;
}
if (aSampleRate != IdealAudioRate()) {
// TODO: Add support for running OfflineAudioContext at other sampling rates
if (aSampleRate <= 0.0f) {
// The DOM binding protects us against infinity and NaN
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
}

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

@ -105,7 +105,7 @@ public:
float SampleRate() const
{
return float(IdealAudioRate());
return mSampleRate;
}
double CurrentTime() const;
@ -186,7 +186,6 @@ public:
void StartRendering();
IMPL_EVENT_HANDLER(complete)
uint32_t GetRate() const { return IdealAudioRate(); }
bool IsOffline() const { return mIsOffline; }
MediaStreamGraph* Graph() const;
@ -204,6 +203,9 @@ private:
friend struct ::mozilla::WebAudioDecodeJob;
private:
// Note that it's important for mSampleRate to be initialized before
// mDestination, as mDestination's constructor needs to access it!
const float mSampleRate;
nsRefPtr<AudioDestinationNode> mDestination;
nsRefPtr<AudioListener> mListener;
MediaBufferDecoder mDecoder;

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

@ -98,7 +98,10 @@ AudioParam::Stream()
}
AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
nsRefPtr<AudioNodeStream> stream = mNode->Context()->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
nsRefPtr<AudioNodeStream> stream =
mNode->Context()->Graph()->CreateAudioNodeStream(engine,
MediaStreamGraph::INTERNAL_STREAM,
Node()->Context()->SampleRate());
// Force the input to have only one channel, and make it down-mix using
// the speaker rules if needed.

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

@ -24,13 +24,14 @@ NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode)
NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode)
void SetParamsOnBiquad(WebCore::Biquad& aBiquad,
float aSampleRate,
BiquadFilterType aType,
double aFrequency,
double aQ,
double aGain,
double aDetune)
{
const double nyquist = IdealAudioRate() * 0.5;
const double nyquist = aSampleRate * 0.5;
double normalizedFrequency = aFrequency / nyquist;
if (aDetune) {
@ -102,7 +103,9 @@ public:
NS_ERROR("Bad BiquadFilterNode Int32Parameter");
}
}
void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE
void SetTimelineParameter(uint32_t aIndex,
const AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
MOZ_ASSERT(mSource && mDestination);
switch (aIndex) {
@ -151,7 +154,7 @@ public:
double detune = mDetune.GetValueAtTime(pos);
for (uint32_t i = 0; i < numberOfChannels; ++i) {
SetParamsOnBiquad(mBiquads[i], mType, freq, q, gain, detune);
SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
mBiquads[i].process(static_cast<const float*>(aInput.mChannelData[i]),
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
@ -213,7 +216,7 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
nsAutoArrayPtr<float> frequencies(new float[length]);
float* frequencyHz = aFrequencyHz.Data();
const double nyquist = IdealAudioRate() * 0.5;
const double nyquist = Context()->SampleRate() * 0.5;
// Normalize the frequencies
for (uint32_t i = 0; i < length; ++i) {
@ -228,7 +231,7 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
double detune = mDetune->GetValueAtTime(currentTime);
WebCore::Biquad biquad;
SetParamsOnBiquad(biquad, mType, freq, q, gain, detune);
SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune);
biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data());
}

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

@ -84,7 +84,9 @@ public:
DELAY,
MAX_DELAY
};
void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE
void SetTimelineParameter(uint32_t aIndex,
const AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
switch (aIndex) {
case DELAY:
@ -105,7 +107,7 @@ public:
}
}
bool EnsureBuffer(uint32_t aNumberOfChannels)
bool EnsureBuffer(uint32_t aNumberOfChannels, TrackRate aSampleRate)
{
if (aNumberOfChannels == 0) {
return false;
@ -114,7 +116,7 @@ public:
if (!mBuffer.SetLength(aNumberOfChannels)) {
return false;
}
const int32_t numFrames = NS_lround(mMaxDelay) * IdealAudioRate();
const int32_t numFrames = NS_lround(mMaxDelay) * aSampleRate;
for (uint32_t channel = 0; channel < aNumberOfChannels; ++channel) {
if (!mBuffer[channel].SetLength(numFrames)) {
return false;
@ -144,7 +146,7 @@ public:
if (!mBuffer.IsEmpty() &&
mLeftOverData == INT32_MIN &&
aStream->AllInputsFinished()) {
mLeftOverData = static_cast<int32_t>(mCurrentDelayTime * IdealAudioRate()) - WEBAUDIO_BLOCK_SIZE;
mLeftOverData = static_cast<int32_t>(mCurrentDelayTime * aStream->SampleRate()) - WEBAUDIO_BLOCK_SIZE;
if (mLeftOverData > 0) {
nsRefPtr<PlayingRefChanged> refchanged =
@ -163,7 +165,7 @@ public:
}
}
if (!EnsureBuffer(numChannels)) {
if (!EnsureBuffer(numChannels, aStream->SampleRate())) {
aOutput->SetNull(0);
return;
}
@ -173,7 +175,7 @@ public:
double delayTime = 0;
float computedDelay[WEBAUDIO_BLOCK_SIZE];
// Use a smoothing range of 20ms
const double smoothingRate = WebAudioUtils::ComputeSmoothingRate(0.02, IdealAudioRate());
const double smoothingRate = WebAudioUtils::ComputeSmoothingRate(0.02, aStream->SampleRate());
if (mDelay.HasSimpleValue()) {
delayTime = std::max(0.0, std::min(mMaxDelay, double(mDelay.GetValue())));
@ -217,7 +219,7 @@ public:
// from currentDelayTime seconds in the past. We also interpolate the two input
// frames in case the read position does not match an integer index.
double readPosition = writeIndex + bufferLength -
(currentDelayTime * IdealAudioRate());
(currentDelayTime * aStream->SampleRate());
if (readPosition >= bufferLength) {
readPosition -= bufferLength;
}

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

@ -46,7 +46,7 @@ public:
, mRatio(12.f)
, mAttack(0.003f)
, mRelease(0.25f)
, mCompressor(new DynamicsCompressor(IdealAudioRate(), 2))
, mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2))
{
}
@ -62,7 +62,9 @@ public:
ATTACK,
RELEASE
};
void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE
void SetTimelineParameter(uint32_t aIndex,
const AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
MOZ_ASSERT(mSource && mDestination);
switch (aIndex) {
@ -105,7 +107,7 @@ public:
const uint32_t channelCount = aInput.mChannelData.Length();
if (mCompressor->numberOfChannels() != channelCount) {
// Create a new compressor object with a new channel count
mCompressor = new WebCore::DynamicsCompressor(IdealAudioRate(),
mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(),
aInput.mChannelData.Length());
}

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

@ -34,7 +34,9 @@ public:
enum Parameters {
GAIN
};
void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE
void SetTimelineParameter(uint32_t aIndex,
const AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
switch (aIndex) {
case GAIN:

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

@ -20,24 +20,26 @@ struct ConvertTimeToTickHelper
{
ConvertTimeToTickHelper* This = static_cast<ConvertTimeToTickHelper*> (aClosure);
if (This->mSourceStream) {
MOZ_ASSERT(This->mSourceStream->SampleRate() == This->mDestinationStream->SampleRate());
return WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(
aTime, This->mSourceStream, This->mDestinationStream);
} else {
StreamTime streamTime = This->mDestinationStream->GetCurrentPosition();
return TimeToTicksRoundUp(IdealAudioRate(), streamTime + SecondsToMediaTime(aTime));
TrackRate sampleRate = This->mDestinationStream->SampleRate();
return TimeToTicksRoundUp(sampleRate, streamTime + SecondsToMediaTime(aTime));
}
}
};
TrackTicks
WebAudioUtils::ConvertDestinationStreamTimeToSourceStreamTime(double aTime,
MediaStream* aSource,
AudioNodeStream* aSource,
MediaStream* aDestination)
{
StreamTime streamTime = std::max<MediaTime>(0, SecondsToMediaTime(aTime));
GraphTime graphTime = aDestination->StreamTimeToGraphTime(streamTime);
StreamTime thisStreamTime = aSource->GraphTimeToStreamTimeOptimistic(graphTime);
TrackTicks ticks = TimeToTicksRoundUp(IdealAudioRate(), thisStreamTime);
TrackTicks ticks = TimeToTicksRoundUp(aSource->SampleRate(), thisStreamTime);
return ticks;
}
@ -46,7 +48,8 @@ WebAudioUtils::StreamPositionToDestinationTime(TrackTicks aSourcePosition,
AudioNodeStream* aSource,
AudioNodeStream* aDestination)
{
StreamTime sourceTime = TicksToTimeRoundDown(IdealAudioRate(), aSourcePosition);
MOZ_ASSERT(aSource->SampleRate() == aDestination->SampleRate());
StreamTime sourceTime = TicksToTimeRoundDown(aSource->SampleRate(), aSourcePosition);
GraphTime graphTime = aSource->StreamTimeToGraphTime(sourceTime);
StreamTime destinationTime = aDestination->GraphTimeToStreamTimeOptimistic(graphTime);
return MediaTimeToSeconds(destinationTime);
@ -57,10 +60,11 @@ WebAudioUtils::ConvertAudioParamToTicks(AudioParamTimeline& aParam,
AudioNodeStream* aSource,
AudioNodeStream* aDest)
{
MOZ_ASSERT(!aSource || aSource->SampleRate() == aDest->SampleRate());
ConvertTimeToTickHelper ctth;
ctth.mSourceStream = aSource;
ctth.mDestinationStream = aDest;
aParam.ConvertEventTimesToTicks(ConvertTimeToTickHelper::Convert, &ctth, IdealAudioRate());
aParam.ConvertEventTimesToTicks(ConvertTimeToTickHelper::Convert, &ctth, aDest->SampleRate());
}
}

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

@ -47,7 +47,7 @@ struct WebAudioUtils {
*/
static TrackTicks
ConvertDestinationStreamTimeToSourceStreamTime(double aTime,
MediaStream* aSource,
AudioNodeStream* aSource,
MediaStream* aDestination);
/**

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

@ -23,6 +23,7 @@ MOCHITEST_FILES := \
test_bug867104.html \
test_bug867174.html \
test_bug867203.html \
test_bug875402.html \
test_analyserNode.html \
test_AudioBuffer.html \
test_AudioContext.html \
@ -31,6 +32,7 @@ MOCHITEST_FILES := \
test_audioParamExponentialRamp.html \
test_audioParamLinearRamp.html \
test_audioParamSetCurveAtTime.html \
test_audioParamSetCurveAtTimeZeroDuration.html \
test_audioParamSetTargetAtTime.html \
test_audioParamTimelineDestinationOffset.html \
test_audioBufferSourceNode.html \

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

@ -13,7 +13,7 @@
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
var ctx = new OfflineAudioContext(2, 100, (new AudioContext()).sampleRate);
var ctx = new OfflineAudioContext(2, 100, 22050);
ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets");
var buf = ctx.createBuffer(2, 100, ctx.sampleRate);
@ -23,6 +23,13 @@ addLoadEvent(function() {
}
}
expectException(function() {
new OfflineAudioContext(2, 100, 0);
}, DOMException.SYNTAX_ERR);
expectException(function() {
new OfflineAudioContext(2, 100, -1);
}, DOMException.SYNTAX_ERR);
var src = ctx.createBufferSource();
src.buffer = buf;
src.start(0);

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

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test AudioParam.linearRampToValue</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var T0 = 0;
var gTest = {
length: 2048,
numberOfChannels: 1,
createGraph: function(context) {
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
for (var i = 0; i < 2048; ++i) {
sourceBuffer.getChannelData(0)[i] = 1;
}
var source = context.createBufferSource();
source.buffer = sourceBuffer;
var gain = context.createGain();
gain.gain.setValueCurveAtTime(this.curve, this.T0, 0);
source.connect(gain);
source.start(0);
return gain;
},
createExpectedBuffers: function(context) {
this.T0 = 1024 / context.sampleRate;
this.curve = new Float32Array(100);
for (var i = 0; i < 100; ++i) {
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
}
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
for (var i = 0; i < 1024; ++i) {
expectedBuffer.getChannelData(0)[i] = 1;
}
for (var i = 1024; i < 2048; ++i) {
expectedBuffer.getChannelData(0)[i] = this.curve[99];
}
return expectedBuffer;
},
};
runTest();
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Crashtest for bug 875402</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
SimpleTest.waitForExplicitFinish();
try { o1 = new OfflineAudioContext(1, 10, (new AudioContext()).sampleRate); } catch(e) { }
try { o2 = o1.createJavaScriptNode(); } catch(e) { }
try { o4 = new OfflineAudioContext(1, 10, (new AudioContext()).sampleRate); } catch(e) { }
try { o5 = o1.createChannelSplitter(4); } catch(e) { }
try { o7 = o4.createScriptProcessor(1024, 4, 1); } catch(e) { }
SpecialPowers.forceCC();
SpecialPowers.forceGC();
try { o1.startRendering(); } catch(e) { }
try { o2.connect(o7); } catch(e) { }
try { o7.connect(o4); } catch(e) { }
try { o9 = o4.createScriptProcessor(1024, 1, 4); } catch(e) { }
try { o2.connect(o7); } catch(e) { }
try { o9.connect(o1); } catch(e) { }
setTimeout("try { o2.connect(o9); } catch(e) { } done();",1000)
try { o7.connect(o4); } catch(e) { }
setTimeout("try { o5.disconnect() } catch(e) { }",100)
try { o2.connect(o9); } catch(e) { }
try { o4.startRendering(); } catch(e) { }
try { o2.connect(o9); } catch(e) { }
setTimeout("try { o7.connect(o4); } catch(e) { }",50)
try { o13 = o4.createGain(); } catch(e) { }
setTimeout("try { o7.connect(o13, 0, 0) } catch(e) { }",50)
function done() {
ok(true, "We did not crash.");
SpecialPowers.clearUserPref("media.webaudio.enabled");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

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

@ -156,7 +156,7 @@ function runTest()
runTestOnContext(context, callback, testOutput);
}
function testOnOfflineContext(callback) {
function testOnOfflineContext(callback, sampleRate) {
function testOutput(nodeToInspect, expectedBuffers, callback) {
nodeToInspect.connect(context.destination);
context.oncomplete = function(e) {
@ -180,12 +180,14 @@ function runTest()
};
context.startRendering();
}
var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, 48000);
var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate);
runTestOnContext(context, callback, testOutput);
}
testOnNormalContext(function() {
testOnOfflineContext(done);
testOnOfflineContext(function() {
testOnOfflineContext(done, 44100);
}, 48000);
});
});
}

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

@ -9,6 +9,7 @@ TEST_TOOL_DIRS += ['test']
MODULE = 'content'
EXPORTS += [
'nsDOMTimeEvent.h',
'nsISMILAttr.h',
'nsISMILType.h',
'nsSMILAnimationController.h',

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

@ -7,13 +7,13 @@
#include "nsGUIEvent.h"
#include "nsPresContext.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsDOMClassInfoID.h"
nsDOMTimeEvent::nsDOMTimeEvent(mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext, nsEvent* aEvent)
: nsDOMEvent(aOwner, aPresContext, aEvent ? aEvent : new nsUIEvent(false, 0, 0)),
mDetail(0)
{
SetIsDOMBinding();
if (aEvent) {
mEventIsInternal = false;
} else {
@ -51,11 +51,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(nsDOMTimeEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(nsDOMTimeEvent, nsDOMEvent)
DOMCI_DATA(TimeEvent, nsDOMTimeEvent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMTimeEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMTimeEvent)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TimeEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_IMETHODIMP

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

@ -8,14 +8,15 @@
#include "nsIDOMTimeEvent.h"
#include "nsDOMEvent.h"
#include "mozilla/dom/TimeEventBinding.h"
class nsDOMTimeEvent : public nsDOMEvent,
public nsIDOMTimeEvent
class nsDOMTimeEvent MOZ_FINAL : public nsDOMEvent,
public nsIDOMTimeEvent
{
public:
nsDOMTimeEvent(mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext, nsEvent* aEvent);
// nsISupports interface:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMTimeEvent, nsDOMEvent)
@ -26,6 +27,28 @@ public:
// Forward to base class
NS_FORWARD_TO_NSDOMEVENT
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE
{
return mozilla::dom::TimeEventBinding::Wrap(aCx, aScope, this);
}
int32_t Detail() const
{
return mDetail;
}
nsIDOMWindow* GetView() const
{
return mView;
}
void InitTimeEvent(const nsAString& aType, nsIDOMWindow* aView,
int32_t aDetail, mozilla::ErrorResult& aRv)
{
aRv = InitTimeEvent(aType, aView, aDetail);
}
private:
nsCOMPtr<nsIDOMWindow> mView;
int32_t mDetail;

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

@ -193,7 +193,6 @@
#include "nsIDOMSVGAnimatedInteger.h"
#include "nsIDOMSVGAnimatedNumber.h"
#include "nsIDOMSVGAnimatedString.h"
#include "nsIDOMTimeEvent.h"
#include "nsIDOMSVGLength.h"
#include "nsIDOMSVGNumber.h"
@ -619,9 +618,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(TimeEvent, nsEventSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
// other SVG classes
NS_DEFINE_CLASSINFO_DATA(SVGAnimatedEnumeration, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@ -1719,11 +1715,6 @@ nsDOMClassInfo::Init()
// The SVG document
DOM_CLASSINFO_MAP_BEGIN(TimeEvent, nsIDOMTimeEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMTimeEvent)
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
// other SVG classes
DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedEnumeration, nsIDOMSVGAnimatedEnumeration)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedEnumeration)

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

@ -84,8 +84,6 @@ DOMCI_CLASS(TreeColumn)
DOMCI_CLASS(CSSMozDocumentRule)
DOMCI_CLASS(CSSSupportsRule)
DOMCI_CLASS(TimeEvent)
// other SVG classes
DOMCI_CLASS(SVGAnimatedEnumeration)
DOMCI_CLASS(SVGAnimatedInteger)

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

@ -2791,12 +2791,12 @@ TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom)
{
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(aFrom);
if (!frameLoaderOwner) {
return NULL;
return nullptr;
}
nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
if (!frameLoader) {
return NULL;
return nullptr;
}
nsCOMPtr<EventTarget> target = frameLoader->GetTabChildGlobalAsEventTarget();

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

@ -136,6 +136,12 @@ public:
mBits = 0;
}
bool operator==(const nsEventHandler& aOther) const
{
return
Ptr() && aOther.Ptr() &&
Ptr()->CallbackPreserveColor() == aOther.Ptr()->CallbackPreserveColor();
}
private:
void operator=(const nsEventHandler&) MOZ_DELETE;

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

@ -56,7 +56,7 @@ static already_AddRefed<nsIURI>
GetWindowURI(nsIDOMWindow *aWindow)
{
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
NS_ENSURE_TRUE(pWindow, NULL);
NS_ENSURE_TRUE(pWindow, nullptr);
nsCOMPtr<nsIDocument> doc = pWindow->GetExtantDoc();
nsCOMPtr<nsIURI> uri;
@ -68,7 +68,7 @@ GetWindowURI(nsIDOMWindow *aWindow)
if (!uri) {
nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrincipal =
do_QueryInterface(aWindow);
NS_ENSURE_TRUE(scriptObjPrincipal, NULL);
NS_ENSURE_TRUE(scriptObjPrincipal, nullptr);
// GetPrincipal() will print a warning if the window does not have an outer
// window, so check here for an outer window first. This code is

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

@ -1504,7 +1504,8 @@ enum StringificationBehavior {
// pval must not be null and must point to a rooted JS::Value
static inline bool
ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval,
ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
JS::MutableHandle<JS::Value> pval,
StringificationBehavior nullBehavior,
StringificationBehavior undefinedBehavior,
FakeDependentString& result)
@ -1535,7 +1536,7 @@ ConvertJSValueToString(JSContext* cx, const JS::Value& v, JS::Value* pval,
if (!s) {
return false;
}
pval->setString(s); // Root the new string.
pval.set(JS::StringValue(s)); // Root the new string.
}
size_t len;

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

@ -1047,7 +1047,11 @@ DOMInterfaces = {
'nativeOwnership': 'owned',
},
'TimeRanges': {
'TimeEvent': {
'nativeType': 'nsDOMTimeEvent',
},
'TimeRanges': {
'wrapperCache': False
},

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

@ -2024,8 +2024,9 @@ def CreateBindingJSObject(descriptor, properties, parent):
else:
objDecl = " JSObject *obj;\n"
if descriptor.proxy:
create = """ obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
JS::PrivateValue(aObject), proto, %s);
create = """ JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
proxyPrivateVal, proto, %s);
if (!obj) {
return nullptr;
}
@ -2440,6 +2441,7 @@ class JSToNativeConversionInfo():
${val} replaced by an expression for the JS::Value in question
${valPtr} is a pointer to the JS::Value in question
${valHandle} is a handle to the JS::Value in question
${valMutableHandle} is a mutable handle to the JS::Value in question
${holderName} replaced by the holder's name, if any
${declName} replaced by the declaration's name
${haveValue} replaced by an expression that evaluates to a boolean
@ -2770,6 +2772,7 @@ for (uint32_t i = 0; i < length; ++i) {
"val" : "temp",
"valPtr": "temp.address()",
"valHandle": "temp",
"valMutableHandle": "&temp",
"declName" : "slot",
# We only need holderName here to handle isExternal()
# interfaces, which use an internal holder for the
@ -3223,7 +3226,7 @@ for (uint32_t i = 0; i < length; ++i) {
def getConversionCode(varName):
conversionCode = (
"if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
"if (!ConvertJSValueToString(cx, ${valHandle}, ${valMutableHandle}, %s, %s, %s)) {\n"
"%s\n"
"}" % (nullBehavior, undefinedBehavior, varName,
exceptionCodeIndented.define()))
@ -3717,6 +3720,9 @@ class CGArgumentConverter(CGThing):
self.replacementVariables["valHandle"] = (
"JS::Handle<JS::Value>::fromMarkedLocation(%s)" %
self.replacementVariables["valPtr"])
self.replacementVariables["valMutableHandle"] = (
"JS::MutableHandle<JS::Value>::fromMarkedLocation(%s)" %
self.replacementVariables["valPtr"])
if argument.defaultValue:
self.replacementVariables["haveValue"] = string.Template(
"${index} < ${argc}").substitute(replacer)
@ -3787,6 +3793,8 @@ class CGArgumentConverter(CGThing):
"valPtr": "&" + val,
"valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" %
val),
"valMutableHandle" : ("JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" %
val),
"declName" : "slot",
# We only need holderName here to handle isExternal()
# interfaces, which use an internal holder for the
@ -4831,6 +4839,8 @@ class CGMethodCall(CGThing):
"val" : distinguishingArg,
"valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" %
distinguishingArg),
"valMutableHandle" : ("JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" %
distinguishingArg),
"obj" : "obj"
})
caseBody.append(CGIndenter(testCode, indent));
@ -5750,6 +5760,7 @@ return true;"""
{
"val": "value",
"valHandle": "value",
"valMutableHandle": "JS::MutableHandle<JS::Value>::fromMarkedLocation(pvalue)",
"valPtr": "pvalue",
"declName": "SetAs" + name + "()",
"holderName": "m" + name + "Holder",
@ -6587,6 +6598,7 @@ class CGProxySpecialOperation(CGPerSignatureCall):
"val": "desc->value",
"valPtr": "&desc->value",
"valHandle" : "JS::Handle<JS::Value>::fromMarkedLocation(&desc->value)",
"valMutableHandle" : "JS::MutableHandle<JS::Value>::fromMarkedLocation(&desc->value)",
"obj": "obj"
}
self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
@ -6668,7 +6680,7 @@ class CGProxyNamedOperation(CGProxySpecialOperation):
def define(self):
# Our first argument is the id we're getting.
argName = self.arguments[0].identifier.name
return (("JS::Value nameVal = %s;\n"
return (("JS::Rooted<JS::Value> nameVal(cx, %s);\n"
"FakeDependentString %s;\n"
"if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n"
" eStringify, eStringify, %s)) {\n"
@ -7781,6 +7793,7 @@ class CGDictionary(CGThing):
replacements = { "val": "temp",
"valPtr": "temp.address()",
"valHandle": "temp",
"valMutableHandle": "&temp",
"declName": self.makeMemberName(member.identifier.name),
# We need a holder name for external interfaces, but
# it's scoped down to the conversion so we can just use
@ -9438,6 +9451,7 @@ class CallbackMember(CGNativeMember):
"val": "rval",
"valPtr": "rval.address()",
"valHandle": "rval",
"valMutableHandle": "&rval",
"holderName" : "rvalHolder",
"declName" : "rvalDecl",
# We actually want to pass in a null scope object here, because

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

@ -388,175 +388,19 @@ Telephony::StopTone()
}
NS_IMPL_EVENT_HANDLER(Telephony, incoming)
NS_IMPL_EVENT_HANDLER(Telephony, callschanged)
NS_IMETHODIMP
Telephony::GetOncallschanged(JSContext* aCx, JS::Value* aValue)
{
GetEventHandler(nsGkAtoms::oncallschanged, aCx, aValue);
return NS_OK;
}
NS_IMETHODIMP
Telephony::SetOncallschanged(JSContext* aCx, const JS::Value& aValue)
{
JS::Value value;
GetEventHandler(nsGkAtoms::oncallschanged, aCx, &value);
if (aValue == value) {
// The event handler is being set to itself.
return NS_OK;
}
nsresult rv = SetEventHandler(nsGkAtoms::oncallschanged, aCx, aValue);
if (NS_FAILED(rv)) {
return rv;
}
// Fire oncallschanged on the next tick if the calls array is ready.
EnqueueEnumerationAck();
return NS_OK;
}
// nsIDOMEventTarget
NS_IMETHODIMP
Telephony::AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener, bool aUseCapture,
bool aWantsUntrusted, uint8_t aArgc)
{
nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType, aListener,
aUseCapture,
aWantsUntrusted,
aArgc);
NS_ENSURE_SUCCESS(rv, rv);
if (aType.EqualsLiteral("callschanged")) {
// Fire oncallschanged on the next tick if the calls array is ready.
EnqueueEnumerationAck();
}
return NS_OK;
}
// EventTarget
void
Telephony::AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener, bool aUseCapture,
const Nullable<bool>& aWantsUntrusted,
ErrorResult& aRv)
Telephony::EventListenerAdded(nsIAtom* aType)
{
nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
aWantsUntrusted, aRv);
if (aRv.Failed()) {
return;
}
if (aType.EqualsLiteral("callschanged")) {
if (aType == nsGkAtoms::oncallschanged) {
// Fire oncallschanged on the next tick if the calls array is ready.
EnqueueEnumerationAck();
}
}
NS_IMETHODIMP
Telephony::AddSystemEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture, bool aWantsUntrusted,
uint8_t aArgc)
{
nsresult rv = nsDOMEventTargetHelper::AddSystemEventListener(aType, aListener,
aUseCapture,
aWantsUntrusted,
aArgc);
NS_ENSURE_SUCCESS(rv, rv);
if (aType.EqualsLiteral("callschanged")) {
// Fire oncallschanged on the next tick if the calls array is ready.
EnqueueEnumerationAck();
}
return NS_OK;
}
NS_IMETHODIMP
Telephony::RemoveEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture)
{
return nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
}
NS_IMETHODIMP
Telephony::RemoveSystemEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture)
{
return nsDOMEventTargetHelper::RemoveSystemEventListener(aType, aListener,
aUseCapture);
}
NS_IMETHODIMP
Telephony::DispatchEvent(nsIDOMEvent* aEvt, bool* aRetval)
{
return nsDOMEventTargetHelper::DispatchEvent(aEvt, aRetval);
}
EventTarget*
Telephony::GetTargetForDOMEvent()
{
return nsDOMEventTargetHelper::GetTargetForDOMEvent();
}
EventTarget*
Telephony::GetTargetForEventTargetChain()
{
return nsDOMEventTargetHelper::GetTargetForEventTargetChain();
}
nsresult
Telephony::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
}
nsresult
Telephony::WillHandleEvent(nsEventChainPostVisitor& aVisitor)
{
return nsDOMEventTargetHelper::WillHandleEvent(aVisitor);
}
nsresult
Telephony::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
return nsDOMEventTargetHelper::PostHandleEvent(aVisitor);
}
nsresult
Telephony::DispatchDOMEvent(nsEvent* aEvent, nsIDOMEvent* aDOMEvent,
nsPresContext* aPresContext,
nsEventStatus* aEventStatus)
{
return nsDOMEventTargetHelper::DispatchDOMEvent(aEvent, aDOMEvent,
aPresContext,
aEventStatus);
}
nsEventListenerManager*
Telephony::GetListenerManager(bool aMayCreate)
{
return nsDOMEventTargetHelper::GetListenerManager(aMayCreate);
}
nsIScriptContext*
Telephony::GetContextForEventHandlers(nsresult* aRv)
{
return nsDOMEventTargetHelper::GetContextForEventHandlers(aRv);
}
JSContext*
Telephony::GetJSContextForEventHandlers()
{
return nsDOMEventTargetHelper::GetJSContextForEventHandlers();
}
// nsITelephonyListener
NS_IMETHODIMP

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

@ -50,19 +50,11 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMTELEPHONY
NS_DECL_NSITELEPHONYLISTENER
NS_DECL_NSIDOMEVENTTARGET
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
Telephony,
nsDOMEventTargetHelper)
using nsDOMEventTargetHelper::RemoveEventListener;
virtual void AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
bool aUseCapture,
const mozilla::dom::Nullable<bool>& aWantsUntrusted,
mozilla::ErrorResult& aRv) MOZ_OVERRIDE;
static already_AddRefed<Telephony>
Create(nsPIDOMWindow* aOwner, nsITelephonyProvider* aProvider);
@ -96,6 +88,8 @@ public:
return mProvider;
}
virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE;
private:
Telephony();
~Telephony();

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

@ -0,0 +1,22 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* For more information on this interface please see
* http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
interface WindowProxy;
interface TimeEvent : Event
{
readonly attribute long detail;
readonly attribute WindowProxy? view;
void initTimeEvent(DOMString aType,
WindowProxy? aView,
long aDetail);
};

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

@ -320,6 +320,7 @@ webidl_files = \
TextTrackCue.webidl \
TextTrackCueList.webidl \
TextTrackList.webidl \
TimeEvent.webidl \
TimeRanges.webidl \
Touch.webidl \
TouchEvent.webidl \

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

@ -1389,7 +1389,7 @@ GLContext::GetTexImage(GLuint aTexture, bool aYInvert, ShaderProgramType aShader
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(size, gfxASurface::ImageFormatARGB32);
if (!surf || surf->CairoStatus()) {
return NULL;
return nullptr;
}
uint32_t currentPackAlignment = 0;

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

@ -214,10 +214,10 @@ public:
/**
* If this TextureImage has a permanent gfxASurface backing,
* return it. Otherwise return NULL.
* return it. Otherwise return nullptr.
*/
virtual already_AddRefed<gfxASurface> GetBackingSurface()
{ return NULL; }
{ return nullptr; }
const nsIntSize& GetSize() const { return mSize; }
ContentType GetContentType() const { return mContentType; }

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

@ -102,11 +102,6 @@ Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
}
void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) {
if (mPos == aPos) {
// Does not make sense to calculate velocity when distance is 0
return;
}
float newVelocity = (mPos - aPos) / aTimeDelta.ToMilliseconds();
bool curVelocityBelowThreshold = fabsf(newVelocity) < gVelocityThreshold;

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

@ -160,7 +160,7 @@ gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
*dx = *dy = 0;
}
// An Azure context doesn't have a surface backing it.
return NULL;
return nullptr;
}
}
@ -249,7 +249,7 @@ already_AddRefed<gfxPath> gfxContext::CopyPath() const
return path.forget();
} else {
// XXX - This is not yet supported for Azure.
return NULL;
return nullptr;
}
}
@ -1711,7 +1711,7 @@ gfxContext::GetFlattenedPath()
return path.forget();
} else {
// XXX - Used by SVG, needs fixing.
return NULL;
return nullptr;
}
}

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

@ -601,9 +601,7 @@ gfxFT2Font::GetOrMakeFont(FT2FontEntry *aFontEntry, const gfxFontStyle *aStyle,
return nullptr;
gfxFontCache::GetCache()->AddNew(font);
}
gfxFont *f = nullptr;
font.swap(f);
return static_cast<gfxFT2Font *>(f);
return font.forget().downcast<gfxFT2Font>();
}
void

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

@ -566,16 +566,6 @@ gfxFontEntry::ReleaseGrFace(gr_face *aFace)
}
}
void
gfxFontEntry::DisconnectSVG()
{
if (mSVGGlyphs) {
delete mSVGGlyphs;
mSVGGlyphs = nullptr;
mSVGInitialized = false;
}
}
void
gfxFontEntry::CheckForGraphiteTables()
{
@ -1280,10 +1270,20 @@ gfxFontCache::MemoryReporter::CollectReports
return NS_OK;
}
NS_IMPL_ISUPPORTS1(gfxFontCache::Observer, nsIObserver)
// Observer for the memory-pressure notification, to trigger
// flushing of the shaped-word caches
class MemoryPressureObserver MOZ_FINAL : public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
NS_IMPL_ISUPPORTS2(MemoryPressureObserver, nsIObserver, nsISupportsWeakReference)
NS_IMETHODIMP
gfxFontCache::Observer::Observe(nsISupports *aSubject,
MemoryPressureObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *someData)
{
@ -1292,8 +1292,6 @@ gfxFontCache::Observer::Observe(nsISupports *aSubject,
if (fontCache) {
fontCache->FlushShapedWordCaches();
}
} else {
NS_NOTREACHED("unexpected notification topic");
}
return NS_OK;
}
@ -1336,7 +1334,7 @@ gfxFontCache::gfxFontCache()
nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (obs) {
obs->AddObserver(new Observer, "memory-pressure", false);
obs->AddObserver(new MemoryPressureObserver, "memory-pressure", false);
}
#if 0 // disabled due to crashiness, see bug 717175

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

@ -24,7 +24,6 @@
#include "gfxPattern.h"
#include "mozilla/HashFunctions.h"
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
#include "gfxFontFeatures.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/Attributes.h"
@ -410,10 +409,7 @@ public:
// Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
gr_face* GetGrFace();
virtual void ReleaseGrFace(gr_face* aFace);
// Release any SVG-glyphs document this font may have loaded.
void DisconnectSVG();
// For memory reporting
virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
FontListSizes* aSizes) const;
@ -967,15 +963,6 @@ protected:
NS_DECL_NSIMEMORYMULTIREPORTER
};
// Observer for notifications that the font cache cares about
class Observer MOZ_FINAL
: public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
void DestroyFont(gfxFont *aFont);
static gfxFontCache *gGlobalCache;

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

@ -388,7 +388,7 @@ gfxPattern::GetSurface()
} else {
// We should never be trying to get the surface off an Azure gfx Pattern.
NS_ERROR("Attempt to get surface off an Azure gfxPattern!");
return NULL;
return nullptr;
}
}

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

@ -741,7 +741,7 @@ gfxPlatform::GetThebesSurfaceForDrawTarget(DrawTarget *aTarget)
RefPtr<DataSourceSurface> data = source->GetDataSurface();
if (!data) {
return NULL;
return nullptr;
}
IntSize size = data->GetSize();

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

@ -95,12 +95,9 @@ already_AddRefed<gfxASurface>
gfxPlatformMac::CreateOffscreenSurface(const gfxIntSize& size,
gfxASurface::gfxContentType contentType)
{
gfxASurface *newSurface = nullptr;
newSurface = new gfxQuartzSurface(size, OptimalFormatForContent(contentType));
NS_IF_ADDREF(newSurface);
return newSurface;
nsRefPtr<gfxASurface> newSurface =
new gfxQuartzSurface(size, OptimalFormatForContent(contentType));
return newSurface.forget();
}
already_AddRefed<gfxASurface>
@ -127,8 +124,8 @@ gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
isurf = new gfxImageSurface (surfaceSize, format);
if (!isurf->CopyFrom (aSurface)) {
// don't even bother doing anything more
NS_ADDREF(aSurface);
return aSurface;
nsRefPtr<gfxASurface> ret = aSurface;
return ret.forget();
}
}

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

@ -67,8 +67,5 @@ gfxQuartzImageSurface::GetAsImageSurface()
return nullptr;
}
nsRefPtr<gfxASurface> asurf = gfxASurface::Wrap(isurf);
gfxImageSurface *imgsurf = (gfxImageSurface*) asurf.get();
NS_ADDREF(imgsurf);
return imgsurf;
return gfxASurface::Wrap(isurf).downcast<gfxImageSurface>();
}

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

@ -192,10 +192,9 @@ already_AddRefed<gfxImageSurface> gfxQuartzSurface::GetAsImageSurface()
// shares the refcounts of Cairo surfaces. However, Wrap also adds a
// reference to the image. We need to remove one of these references
// explicitly so we don't leak.
gfxImageSurface* imgSurface = static_cast<gfxImageSurface*> (img.forget().get());
imgSurface->Release();
img->Release();
return imgSurface;
return img.forget().downcast<gfxImageSurface>();
}
gfxQuartzSurface::~gfxQuartzSurface()

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

@ -759,14 +759,6 @@ gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry,
return aEntry->mPrivate ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
}
PLDHashOperator
gfxUserFontSet::UserFontCache::Entry::DisconnectSVG(Entry* aEntry,
void* aUserData)
{
aEntry->GetFontEntry()->DisconnectSVG();
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
const char* aTopic,
@ -780,8 +772,6 @@ gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
sUserFonts->Clear();
} else if (!strcmp(aTopic, "last-pb-context-exited")) {
sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr);
} else if (!strcmp(aTopic, "xpcom-shutdown")) {
sUserFonts->EnumerateEntries(Entry::DisconnectSVG, nullptr);
} else {
NS_NOTREACHED("unexpected topic");
}
@ -834,7 +824,6 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry)
obs->AddObserver(flusher, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
false);
obs->AddObserver(flusher, "last-pb-context-exited", false);
obs->AddObserver(flusher, "xpcom-shutdown", false);
}
}

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

@ -342,7 +342,6 @@ public:
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
static PLDHashOperator RemoveIfPrivate(Entry* aEntry, void* aUserData);
static PLDHashOperator DisconnectSVG(Entry* aEntry, void* aUserData);
private:
static uint32_t

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

@ -678,7 +678,9 @@ gfxWindowsPlatform::CreateOffscreenImageSurface(const gfxIntSize& aSize,
{
#ifdef CAIRO_HAS_D2D_SURFACE
if (mRenderMode == RENDER_DIRECT2D) {
return new gfxImageSurface(aSize, OptimalFormatForContent(aContentType));
nsRefPtr<gfxASurface> surface =
new gfxImageSurface(aSize, OptimalFormatForContent(aContentType));
return surface.forget();
}
#endif

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

@ -178,10 +178,7 @@ gfxWindowsSurface::GetAsImageSurface()
if (!isurf)
return nullptr;
nsRefPtr<gfxASurface> asurf = gfxASurface::Wrap(isurf);
gfxImageSurface *imgsurf = (gfxImageSurface*) asurf.get();
NS_ADDREF(imgsurf);
return imgsurf;
return gfxASurface::Wrap(isurf).downcast<gfxImageSurface>();
}
already_AddRefed<gfxWindowsSurface>
@ -202,14 +199,11 @@ gfxWindowsSurface::OptimizeToDDB(HDC dc, const gfxIntSize& size, gfxImageFormat
tmpCtx.SetSource(this);
tmpCtx.Paint();
gfxWindowsSurface *raw = (gfxWindowsSurface*) (wsurf.get());
NS_ADDREF(raw);
// we let the new DDB surfaces be converted back to dibsections if
// acquire_source_image is called on them
cairo_win32_surface_set_can_convert_to_dib(raw->CairoSurface(), TRUE);
cairo_win32_surface_set_can_convert_to_dib(wsurf->CairoSurface(), TRUE);
return raw;
return wsurf.forget().downcast<gfxWindowsSurface>();
}
nsresult

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

@ -228,6 +228,8 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
nsresult imgFrame::Optimize()
{
MOZ_ASSERT(NS_IsMainThread());
if (gDisableOptimize)
return NS_OK;
@ -486,6 +488,7 @@ void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
}
}
// This can be called from any thread, but not simultaneously.
nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
{
mDecoded.UnionRect(mDecoded, aUpdateRect);
@ -495,10 +498,6 @@ nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
nsIntRect boundsRect(mOffset, mSize);
mDecoded.IntersectRect(mDecoded, boundsRect);
#ifdef XP_MACOSX
if (mQuartzSurface)
mQuartzSurface->Flush();
#endif
return NS_OK;
}
@ -575,6 +574,8 @@ void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
nsresult imgFrame::LockImageData()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
if (mLockCount < 0) {
return NS_ERROR_FAILURE;
@ -630,6 +631,8 @@ nsresult imgFrame::LockImageData()
nsresult imgFrame::UnlockImageData()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!");
if (mLockCount == 0) {
return NS_ERROR_FAILURE;
@ -672,6 +675,8 @@ nsresult imgFrame::UnlockImageData()
void imgFrame::MarkImageDataDirty()
{
MOZ_ASSERT(NS_IsMainThread());
if (mImageSurface)
mImageSurface->Flush();

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

@ -10,11 +10,11 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
CSRCS = \
DISABLED_CSRCS = \
umaptable.c \
$(NULL)
SIMPLE_PROGRAMS = $(CSRCS:.c=$(BIN_SUFFIX))
SIMPLE_PROGRAMS = $(DISABLED_CSRCS:.c=$(BIN_SUFFIX))
include $(topsrcdir)/config/rules.mk

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

@ -4,3 +4,6 @@
# 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/.
CSRCS += [
'umaptable.c',
]

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

@ -92,12 +92,11 @@ nsEntityConverter::LoadEntityBundle(uint32_t version)
LossyAppendUTF16toASCII(versionName, url);
url.Append(".properties");
nsIStringBundle* bundle;
rv = bundleService->CreateBundle(url.get(), &bundle);
nsCOMPtr<nsIStringBundle> bundle;
rv = bundleService->CreateBundle(url.get(), getter_AddRefs(bundle));
NS_ENSURE_SUCCESS(rv, nullptr);
// does this addref right?
return bundle;
return bundle.forget();
}
const PRUnichar*

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

@ -231,9 +231,13 @@ class MOZ_STACK_CLASS Handle : public js::HandleBase<T>
typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
const T *address() const { return ptr; }
T get() const { return *ptr; }
const T& get() const { return *ptr; }
operator T() const { return get(); }
/*
* Return a reference so passing a Handle<T> to something that
* takes a |const T&| is not a GC hazard.
*/
operator const T&() const { return get(); }
T operator->() const { return get(); }
bool operator!=(const T &other) { return *ptr != other; }
@ -465,7 +469,11 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
Rooted<T> *previous() { return prev; }
#endif
operator T() const { return ptr; }
/*
* Important: Return a reference here so passing a Rooted<T> to
* something that takes a |const T&| is not a GC hazard.
*/
operator const T&() const { return ptr; }
T operator->() const { return ptr; }
T *address() { return &ptr; }
const T *address() const { return &ptr; }

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

@ -6,7 +6,7 @@
// The mode asserts options object.
#define TRY_PARALLEL(MODE) \
((!MODE || MODE.mode === "par"))
((!MODE || MODE.mode !== "seq"))
#define ASSERT_SEQUENTIAL_IS_OK(MODE) \
do { if (MODE) AssertSequentialIsOK(MODE) } while(false)
@ -297,7 +297,7 @@ function ParallelArrayBuild(self, shape, func, mode) {
var chunks = ComputeNumChunks(length);
var numSlices = ForkJoinSlices();
var info = ComputeAllSliceBounds(chunks, numSlices);
ForkJoin(constructSlice, CheckParallel(mode));
ForkJoin(constructSlice, ForkJoinMode(mode));
return;
}
@ -319,6 +319,8 @@ function ParallelArrayBuild(self, shape, func, mode) {
computefunc(indexStart, indexEnd);
UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos);
}
return chunkEnd == info[SLICE_END(sliceId)];
}
function fill1(indexStart, indexEnd) {
@ -387,7 +389,7 @@ function ParallelArrayMap(func, mode) {
var chunks = ComputeNumChunks(length);
var numSlices = ForkJoinSlices();
var info = ComputeAllSliceBounds(chunks, numSlices);
ForkJoin(mapSlice, CheckParallel(mode));
ForkJoin(mapSlice, ForkJoinMode(mode));
return NewParallelArray(ParallelArrayView, [length], buffer, 0);
}
@ -416,6 +418,8 @@ function ParallelArrayMap(func, mode) {
UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos);
}
return chunkEnd == info[SLICE_END(sliceId)];
}
}
@ -446,7 +450,7 @@ function ParallelArrayReduce(func, mode) {
var info = ComputeAllSliceBounds(chunks, numSlices);
var subreductions = NewDenseArray(numSlices);
ForkJoin(reduceSlice, CheckParallel(mode));
ForkJoin(reduceSlice, ForkJoinMode(mode));
var accumulator = subreductions[0];
for (var i = 1; i < numSlices; i++)
accumulator = func(accumulator, subreductions[i]);
@ -492,6 +496,8 @@ function ParallelArrayReduce(func, mode) {
UnsafeSetElement(subreductions, sliceId, accumulator,
info, SLICE_POS(sliceId), ++chunkPos);
}
return chunkEnd == info[SLICE_END(sliceId)];
}
function reduceChunk(accumulator, from, to) {
@ -533,7 +539,7 @@ function ParallelArrayScan(func, mode) {
var info = ComputeAllSliceBounds(chunks, numSlices);
// Scan slices individually (see comment on phase1()).
ForkJoin(phase1, CheckParallel(mode));
ForkJoin(phase1, ForkJoinMode(mode));
// Compute intermediates array (see comment on phase2()).
var intermediates = [];
@ -553,7 +559,7 @@ function ParallelArrayScan(func, mode) {
info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length);
// Complete each slice using intermediates array (see comment on phase2()).
ForkJoin(phase2, CheckParallel(mode));
ForkJoin(phase2, ForkJoinMode(mode));
return NewParallelArray(ParallelArrayView, [length], buffer, 0);
}
@ -616,6 +622,8 @@ function ParallelArrayScan(func, mode) {
scan(accumulator, indexStart, indexEnd);
UnsafeSetElement(info, SLICE_POS(sliceId), ++chunkPos);
}
return chunkEnd == info[SLICE_END(sliceId)];
}
/**
@ -670,7 +678,7 @@ function ParallelArrayScan(func, mode) {
*/
function phase2(sliceId, numSlices, warmup) {
if (sliceId == 0)
return; // No work to do for the 0th slice.
return true; // No work to do for the 0th slice.
var indexPos = info[SLICE_POS(sliceId)];
var indexEnd = info[SLICE_END(sliceId)];
@ -683,6 +691,8 @@ function ParallelArrayScan(func, mode) {
UnsafeSetElement(buffer, indexPos, func(intermediate, buffer[indexPos]),
info, SLICE_POS(sliceId), indexPos + 1);
}
return indexEnd == info[SLICE_END(sliceId)];
}
}
@ -817,7 +827,7 @@ function ParallelArrayScatter(targets, defaultValue, conflictFunc, length, mode)
UnsafeSetElement(conflicts, i, false);
}
ForkJoin(fill, CheckParallel(mode));
ForkJoin(fill, ForkJoinMode(mode));
return NewParallelArray(ParallelArrayView, [length], buffer, 0);
function fill(sliceId, numSlices, warmup) {
@ -840,6 +850,8 @@ function ParallelArrayScatter(targets, defaultValue, conflictFunc, length, mode)
conflicts, t, true,
checkpoints, sliceId, indexPos + 1);
}
return indexEnd == targetsLength;
}
}
@ -872,7 +884,7 @@ function ParallelArrayScatter(targets, defaultValue, conflictFunc, length, mode)
for (var i = 0; i < length; i++)
UnsafeSetElement(outputBuffer, i, defaultValue);
ForkJoin(fill, CheckParallel(mode));
ForkJoin(fill, ForkJoinMode(mode));
mergeBuffers();
return NewParallelArray(ParallelArrayView, [length], outputBuffer, 0);
@ -893,6 +905,8 @@ function ParallelArrayScatter(targets, defaultValue, conflictFunc, length, mode)
conflicts, t, true,
info, SLICE_POS(sliceId), ++indexPos);
}
return indexEnd == info[SLICE_END(sliceId)];
}
/**
@ -990,7 +1004,7 @@ function ParallelArrayFilter(func, mode) {
for (var i = 0; i < numSlices; i++)
UnsafeSetElement(counts, i, 0);
var survivors = NewDenseArray(chunks);
ForkJoin(findSurvivorsInSlice, CheckParallel(mode));
ForkJoin(findSurvivorsInSlice, ForkJoinMode(mode));
// Step 2. Compress the slices into one contiguous set.
var count = 0;
@ -998,7 +1012,7 @@ function ParallelArrayFilter(func, mode) {
count += counts[i];
var buffer = NewDenseArray(count);
if (count > 0)
ForkJoin(copySurvivorsInSlice, CheckParallel(mode));
ForkJoin(copySurvivorsInSlice, ForkJoinMode(mode));
return NewParallelArray(ParallelArrayView, [count], buffer, 0);
}
@ -1020,7 +1034,6 @@ function ParallelArrayFilter(func, mode) {
* the next chunk sliceId, lest we should bail.
*/
function findSurvivorsInSlice(sliceId, numSlices, warmup) {
var chunkPos = info[SLICE_POS(sliceId)];
var chunkEnd = info[SLICE_END(sliceId)];
@ -1043,6 +1056,8 @@ function ParallelArrayFilter(func, mode) {
counts, sliceId, count,
info, SLICE_POS(sliceId), ++chunkPos);
}
return chunkEnd == info[SLICE_END(sliceId)];
}
function copySurvivorsInSlice(sliceId, numSlices, warmup) {
@ -1051,12 +1066,6 @@ function ParallelArrayFilter(func, mode) {
// user code. Therefore, we don't expect bailouts and make an
// effort to proceed chunk by chunk or avoid duplicating work.
// During warmup, we only execute with sliceId 0. This would fail to
// execute the loop below. Therefore, during warmup, we
// substitute 1 for the sliceId.
if (warmup && sliceId == 0 && numSlices != 1)
sliceId = 1;
// Total up the items preserved by previous slices.
var count = 0;
if (sliceId > 0) { // FIXME(#819219)---work around a bug in Ion's range checks
@ -1067,7 +1076,7 @@ function ParallelArrayFilter(func, mode) {
// Compute the final index we expect to write.
var total = count + counts[sliceId];
if (count == total)
return;
return true;
// Iterate over the chunks assigned to us. Read the bitset for
// each chunk. Copy values where a 1 appears until we have
@ -1090,6 +1099,8 @@ function ParallelArrayFilter(func, mode) {
}
}
}
return true;
}
}
@ -1251,29 +1262,21 @@ function AssertSequentialIsOK(mode) {
ThrowError(JSMSG_WRONG_VALUE, "parallel execution", "sequential was forced");
}
/**
* Internal debugging tool: returns a function to be supplied to
* ForkJoin() that will check that the parallel results
* bailout/succeed as expected. Returns null if no mode is supplied
* or we are building with some strange IF_DEF configuration such that
* we don't expect parallel execution to work.
*/
function CheckParallel(mode) {
if (!mode || !ParallelTestsShouldPass())
return null;
return function(result, bailouts, causes) {
if (!("expect" in mode) || mode.expect === "any") {
return; // Ignore result when unspecified or unimportant.
} else if (mode.expect === "mixed" && result !== "disqualified") {
return; // "mixed" means that it may bailout, may succeed
} else if (result === mode.expect) {
return;
}
ThrowError(JSMSG_WRONG_VALUE, mode.expect,
result+":"+bailouts+":"+causes);
};
function ForkJoinMode(mode) {
// WARNING: this must match the enum ForkJoinMode in ForkJoin.cpp
if (!mode || !mode.mode) {
return 0;
} else if (mode.mode === "compile") {
return 1;
} else if (mode.mode === "par") {
return 2;
} else if (mode.mode === "recover") {
return 3;
} else if (mode.mode === "bailout") {
return 4;
} else {
ThrowError(JSMSG_PAR_ARRAY_BAD_ARG, "");
}
}
/*

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

@ -80,9 +80,8 @@ function List() {}
ListProto.push = std_Array_push;
ListProto.slice = std_Array_slice;
ListProto.sort = std_Array_sort;
List.prototype = ListProto;
MakeConstructible(List, ListProto);
}
MakeConstructible(List);
/********** Record specification type **********/
@ -92,7 +91,7 @@ MakeConstructible(List);
function Record() {
return std_Object_create(null);
}
MakeConstructible(Record);
MakeConstructible(Record, {});
/********** Abstract operations defined in ECMAScript Language Specification **********/

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

@ -4601,7 +4601,8 @@ CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo
// Memory for the objects is provided by the LifoAlloc argument,
// which may be explicitly tracked by the caller.
MIRGraph *graph = lifo.new_<MIRGraph>(tempAlloc);
CompileInfo *info = lifo.new_<CompileInfo>(locals.count());
CompileInfo *info = lifo.new_<CompileInfo>(locals.count(),
SequentialExecution);
MIRGenerator *mirGen = lifo.new_<MIRGenerator>(m.cx()->compartment, tempAlloc, graph, info);
JS_ASSERT(tempAlloc && graph && info && mirGen);

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

@ -62,286 +62,6 @@ IonBailoutIterator::dump() const
}
}
static JSScript *
GetBailedJSScript(JSContext *cx)
{
// Just after the frame conversion, we can safely interpret the ionTop as JS
// frame because it targets the bailed JS frame converted to an exit frame.
IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->mainThread().ionTop);
switch (GetCalleeTokenTag(frame->calleeToken())) {
case CalleeToken_Function: {
JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
return fun->nonLazyScript();
}
case CalleeToken_Script:
return CalleeTokenToScript(frame->calleeToken());
default:
JS_NOT_REACHED("unexpected callee token kind");
return NULL;
}
}
void
StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
{
uint32_t exprStackSlots = iter.slots() - script()->nfixed;
#ifdef TRACK_SNAPSHOTS
iter.spewBailingFrom();
#endif
IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u",
exprStackSlots, isFunctionFrame());
if (iter.bailoutKind() == Bailout_ArgumentCheck) {
// Temporary hack -- skip the (unused) scopeChain, because it could be
// bogus (we can fail before the scope chain slot is set). Strip the
// hasScopeChain flag. If a call object is needed, it will get handled later
// by |ThunkToInterpreter| which call |EnsureHasScopeObjects|.
iter.skip();
flags_ &= ~StackFrame::HAS_SCOPECHAIN;
// If the script binds arguments, then skip the snapshot slot reserved to hold
// its value.
if (script()->argumentsHasVarBinding())
iter.skip();
flags_ &= ~StackFrame::HAS_ARGS_OBJ;
} else {
Value scopeChain = iter.read();
JS_ASSERT(scopeChain.isObject() || scopeChain.isUndefined());
if (scopeChain.isObject()) {
scopeChain_ = &scopeChain.toObject();
flags_ |= StackFrame::HAS_SCOPECHAIN;
if (isFunctionFrame() && fun()->isHeavyweight())
flags_ |= StackFrame::HAS_CALL_OBJ;
}
// The second slot will be an arguments object if the script needs one.
if (script()->argumentsHasVarBinding()) {
Value argsObj = iter.read();
JS_ASSERT(argsObj.isObject() || argsObj.isUndefined());
if (argsObj.isObject())
initArgsObj(argsObj.toObject().asArguments());
}
}
// Assume that all new stack frames have had their entry flag set if
// profiling has been turned on. This will be corrected if necessary
// elsewhere.
if (cx->runtime->spsProfiler.enabled())
setPushedSPSFrame();
if (isFunctionFrame()) {
Value thisv = iter.read();
formals()[-1] = thisv;
// The new |this| must have already been constructed prior to an Ion
// constructor running.
if (isConstructing())
JS_ASSERT(!thisv.isPrimitive());
JS_ASSERT(iter.slots() >= CountArgSlots(script(), fun()));
IonSpew(IonSpew_Bailouts, " frame slots %u, nargs %u, nfixed %u",
iter.slots(), fun()->nargs, script()->nfixed);
for (uint32_t i = 0; i < fun()->nargs; i++) {
Value arg = iter.read();
formals()[i] = arg;
}
}
exprStackSlots -= CountArgSlots(script(), maybeFun());
for (uint32_t i = 0; i < script()->nfixed; i++) {
Value slot = iter.read();
slots()[i] = slot;
}
IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots);
FrameRegs &regs = cx->regs();
for (uint32_t i = 0; i < exprStackSlots; i++) {
Value v;
// If coming from an invalidation bailout, and this is the topmost
// value, and a value override has been specified, don't read from the
// iterator. Otherwise, we risk using a garbage value.
if (!iter.moreFrames() && i == exprStackSlots - 1 && cx->runtime->hasIonReturnOverride())
v = iter.skip();
else
v = iter.read();
*regs.sp++ = v;
}
unsigned pcOff = iter.pcOffset();
regs.pc = script()->code + pcOff;
if (iter.resumeAfter())
regs.pc = GetNextPc(regs.pc);
IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)",
pcOff, (void *)script(), PCToLineNumber(script(), regs.pc));
// For fun.apply({}, arguments) the reconstructStackDepth will be atleast 4,
// but it could be that we inlined the funapply. In that case exprStackSlots,
// will have the real arguments in the slots and not always be equal.
JS_ASSERT_IF(JSOp(*regs.pc) != JSOP_FUNAPPLY,
exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc));
}
static StackFrame *
PushInlinedFrame(JSContext *cx, StackFrame *callerFrame)
{
// Grab the callee object out of the caller's frame, which has already been restored.
// N.B. we currently assume that the caller frame is at a JSOP_CALL pc for the caller frames,
// which will not be the case when we inline getters (in which case it would be a
// JSOP_GETPROP). That will have to be handled differently.
FrameRegs &regs = cx->regs();
JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_INVOKE);
int callerArgc = GET_ARGC(regs.pc);
if (JSOp(*regs.pc) == JSOP_FUNAPPLY)
callerArgc = callerFrame->nactual();
const Value &calleeVal = regs.sp[-callerArgc - 2];
RootedFunction fun(cx, calleeVal.toObject().toFunction());
RootedScript script(cx, fun->nonLazyScript());
CallArgs inlineArgs = CallArgsFromSp(callerArgc, regs.sp);
// Bump the stack pointer to make it look like the inline args have been pushed, but they will
// really get filled in by RestoreOneFrame.
regs.sp = inlineArgs.end();
InitialFrameFlags flags = INITIAL_NONE;
if (JSOp(*regs.pc) == JSOP_NEW)
flags = INITIAL_CONSTRUCT;
if (!cx->stack.pushInlineFrame(cx, regs, inlineArgs, fun, script, flags, DONT_REPORT_ERROR))
return NULL;
StackFrame *fp = cx->stack.fp();
JS_ASSERT(fp == regs.fp());
JS_ASSERT(fp->prev() == callerFrame);
fp->formals()[-2].setObject(*fun);
return fp;
}
static uint32_t
ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it)
{
IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p",
it.script()->filename(), it.script()->lineno, (void *) it.ionScript());
IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u",
it.snapshotOffset(), it.ionScript()->snapshotsSize());
#ifdef DEBUG
// Use count is reset after invalidation. Log use count on bailouts to
// determine if we have a critical sequence of bailout.
//
// Note: frame conversion only occurs in sequential mode
if (it.script()->maybeIonScript() == it.ionScript()) {
IonSpew(IonSpew_Bailouts, " Current script use count is %u",
it.script()->getUseCount());
}
#endif
// Set a flag to avoid bailing out on every iteration or function call. Ion can
// compile and run the script again after an invalidation.
it.ionScript()->incNumBailouts();
it.script()->updateBaselineOrIonRaw();
// We use OffTheBooks instead of cx because at this time we cannot iterate
// on the stack safely and the reported error attempts to walk the IonMonkey
// frames. We cannot iterate on the stack because we have no exit frame to
// start Ion frames iteratons.
BailoutClosure *br = js_new<BailoutClosure>();
if (!br)
return BAILOUT_RETURN_FATAL_ERROR;
activation->setBailout(br);
StackFrame *fp;
if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) {
// Avoid creating duplicate interpreter frames. This is necessary to
// avoid blowing out the interpreter stack, and must be used in
// conjunction with inline-OSR from within bailouts (since each Ion
// activation must be tied to a unique StackFrame for ScriptFrameIter to
// work).
//
// Note: If the entry frame is a placeholder (a stub frame pushed for
// JM -> Ion calls), then we cannot re-use it as it does not have
// enough slots.
JS_ASSERT(cx->fp() == activation->entryfp());
fp = cx->fp();
cx->regs().sp = fp->base();
} else {
br->constructFrame();
if (!cx->stack.pushBailoutArgs(cx, it, br->argsGuard()))
return BAILOUT_RETURN_FATAL_ERROR;
fp = cx->stack.pushBailoutFrame(cx, it, *br->argsGuard(), br->frameGuard());
}
if (!fp)
return BAILOUT_RETURN_OVERRECURSED;
br->setEntryFrame(fp);
JSFunction *callee = it.maybeCallee();
if (callee)
fp->formals()[-2].setObject(*callee);
if (it.isConstructing())
fp->setConstructing();
SnapshotIterator iter(it);
while (true) {
IonSpew(IonSpew_Bailouts, " restoring frame");
fp->initFromBailout(cx, iter);
// If the IonScript wasn't compiled with SPS enabled, make sure that the StackFrame
// frame isn't marked as having a pushed SPS frame.
if (!it.ionScript()->hasSPSInstrumentation())
fp->unsetPushedSPSFrame();
if (!iter.moreFrames())
break;
iter.nextFrame();
fp = PushInlinedFrame(cx, fp);
if (!fp)
return BAILOUT_RETURN_OVERRECURSED;
}
fp->clearRunningInIon();
jsbytecode *bailoutPc = fp->script()->code + iter.pcOffset();
br->setBailoutPc(bailoutPc);
switch (iter.bailoutKind()) {
case Bailout_Normal:
return BAILOUT_RETURN_OK;
case Bailout_TypeBarrier:
return BAILOUT_RETURN_TYPE_BARRIER;
case Bailout_Monitor:
return BAILOUT_RETURN_MONITOR;
case Bailout_BoundsCheck:
return BAILOUT_RETURN_BOUNDS_CHECK;
case Bailout_ShapeGuard:
return BAILOUT_RETURN_SHAPE_GUARD;
case Bailout_CachedShapeGuard:
return BAILOUT_RETURN_CACHED_SHAPE_GUARD;
// When bailing out from an argument check, none of the code of the
// function has run yet. When profiling, this means that the function
// hasn't flagged its entry just yet. It has been "entered," however, so
// we flag it here manually that the entry has happened.
case Bailout_ArgumentCheck:
fp->unsetPushedSPSFrame();
Probes::enterScript(cx, fp->script(), fp->script()->function(), fp);
return BAILOUT_RETURN_ARGUMENT_CHECK;
}
JS_NOT_REACHED("bad bailout kind");
return BAILOUT_RETURN_FATAL_ERROR;
}
uint32_t
ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
{
@ -353,25 +73,18 @@ ion::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
IonBailoutIterator iter(ionActivations, sp);
IonActivation *activation = ionActivations.activation();
// IonCompartment *ioncompartment = cx->compartment->ionCompartment();
// IonActivation *activation = cx->runtime->ionActivation;
// FrameRecovery in = FrameRecoveryFromBailout(ioncompartment, sp);
IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
uint32_t retval;
if (IsBaselineEnabled(cx)) {
*bailoutInfo = NULL;
retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
JS_ASSERT(retval == BAILOUT_RETURN_BASELINE ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL);
} else {
retval = ConvertFrames(cx, activation, iter);
}
JS_ASSERT(IsBaselineEnabled(cx));
if (retval != BAILOUT_RETURN_BASELINE)
*bailoutInfo = NULL;
uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo);
JS_ASSERT(retval == BAILOUT_RETURN_OK ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != NULL);
if (retval != BAILOUT_RETURN_OK)
EnsureExitFrame(iter.jsFrame());
return retval;
@ -396,36 +109,16 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
// Note: the frame size must be computed before we return from this function.
*frameSizeOut = iter.topFrameSize();
uint32_t retval;
if (IsBaselineEnabled(cx)) {
*bailoutInfo = NULL;
retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo);
JS_ASSERT(retval == BAILOUT_RETURN_BASELINE ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL);
JS_ASSERT(IsBaselineEnabled(cx));
if (retval != BAILOUT_RETURN_BASELINE) {
IonJSFrameLayout *frame = iter.jsFrame();
IonSpew(IonSpew_Invalidate, "converting to exit frame");
IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken());
IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress());
frame->replaceCalleeToken(NULL);
EnsureExitFrame(frame);
IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken());
IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize()));
IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress());
}
iter.ionScript()->decref(cx->runtime->defaultFreeOp());
return retval;
} else {
retval = ConvertFrames(cx, activation, iter);
*bailoutInfo = NULL;
uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo);
JS_ASSERT(retval == BAILOUT_RETURN_OK ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != NULL);
if (retval != BAILOUT_RETURN_OK) {
IonJSFrameLayout *frame = iter.jsFrame();
IonSpew(IonSpew_Invalidate, "converting to exit frame");
IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken());
@ -438,83 +131,11 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken());
IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize()));
IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress());
iter.ionScript()->decref(cx->runtime->defaultFreeOp());
// Only need to take ion return override if resuming to interpreter.
if (cx->runtime->hasIonReturnOverride())
cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride();
if (retval != BAILOUT_RETURN_FATAL_ERROR) {
// If invalidation was triggered inside a stub call, we may still have to
// monitor the result, since the bailout happens before the MMonitorTypes
// instruction is executed.
jsbytecode *pc = activation->bailout()->bailoutPc();
// If this is not a ResumeAfter bailout, there's nothing to monitor,
// we will redo the op in the interpreter.
bool isResumeAfter = GetNextPc(pc) == cx->regs().pc;
if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) {
JS_ASSERT(retval == BAILOUT_RETURN_OK);
return BAILOUT_RETURN_MONITOR;
}
return retval;
}
return BAILOUT_RETURN_FATAL_ERROR;
}
}
static void
ReflowArgTypes(JSContext *cx)
{
StackFrame *fp = cx->fp();
unsigned nargs = fp->fun()->nargs;
RootedScript script(cx, fp->script());
types::AutoEnterAnalysis enter(cx);
if (!fp->isConstructing())
types::TypeScript::SetThis(cx, script, fp->thisValue());
for (unsigned i = 0; i < nargs; ++i)
types::TypeScript::SetArgument(cx, script, i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING));
}
uint32_t
ion::ReflowTypeInfo(uint32_t bailoutResult)
{
JSContext *cx = GetIonContext()->cx;
IonActivation *activation = cx->mainThread().ionActivation;
IonSpew(IonSpew_Bailouts, "reflowing type info");
if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) {
IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry");
ReflowArgTypes(cx);
return true;
}
RootedScript script(cx, cx->fp()->script());
jsbytecode *pc = activation->bailout()->bailoutPc();
iter.ionScript()->decref(cx->runtime->defaultFreeOp());
JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
IonSpew(IonSpew_Bailouts, "reflowing type info at %s:%d pcoff %d", script->filename(),
script->lineno, pc - script->code);
types::AutoEnterAnalysis enter(cx);
if (bailoutResult == BAILOUT_RETURN_TYPE_BARRIER)
script->analysis()->breakTypeBarriers(cx, pc - script->code, false);
else
JS_ASSERT(bailoutResult == BAILOUT_RETURN_MONITOR);
// When a type barrier fails, the bad value is at the top of the stack.
Value &result = cx->regs().sp[-1];
types::TypeScript::Monitor(cx, script, pc, result);
return true;
return retval;
}
// Initialize the decl env Object, call object, and any arguments obj of the current frame.
@ -530,161 +151,6 @@ ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
return true;
}
uint32_t
ion::BoundsCheckFailure()
{
JSContext *cx = GetIonContext()->cx;
JSScript *script = GetBailedJSScript(cx);
IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename(),
script->lineno);
if (!script->failedBoundsCheck) {
script->failedBoundsCheck = true;
// Invalidate the script to force a recompile.
IonSpew(IonSpew_Invalidate, "Invalidating due to bounds check failure");
return Invalidate(cx, script);
}
return true;
}
uint32_t
ion::ShapeGuardFailure()
{
JSContext *cx = GetIonContext()->cx;
JSScript *script = GetBailedJSScript(cx);
JS_ASSERT(!script->ionScript()->invalidated());
script->failedShapeGuard = true;
IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure");
return Invalidate(cx, script);
}
uint32_t
ion::CachedShapeGuardFailure()
{
JSContext *cx = GetIonContext()->cx;
JSScript *script = GetBailedJSScript(cx);
JS_ASSERT(!script->ionScript()->invalidated());
script->failedShapeGuard = true;
IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure");
return Invalidate(cx, script);
}
uint32_t
ion::ThunkToInterpreter(Value *vp)
{
JSContext *cx = GetIonContext()->cx;
IonActivation *activation = cx->mainThread().ionActivation;
BailoutClosure *br = activation->takeBailout();
InterpMode resumeMode = JSINTERP_BAILOUT;
if (!EnsureHasScopeObjects(cx, cx->fp()))
resumeMode = JSINTERP_RETHROW;
// By default we set the forbidOsr flag on the ion script, but if a GC
// happens just after we re-enter the interpreter, the ion script get
// invalidated and we do not see the forbidOsr flag. This may cause a loop
// which apear with eager compilation and gc zeal enabled. This code is a
// workaround to avoid recompiling with OSR just after a bailout followed by
// a GC. (see Bug 746691 & Bug 751383)
jsbytecode *pc = cx->regs().pc;
while (JSOp(*pc) == JSOP_GOTO)
pc += GET_JUMP_OFFSET(pc);
if (JSOp(*pc) == JSOP_LOOPENTRY)
cx->regs().pc = GetNextPc(pc);
// When JSScript::argumentsOptimizationFailed, we cannot access Ion frames
// in order to create an arguments object for them. However, there is an
// invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the
// prologue), so we must create one now for each inlined frame which needs
// one.
{
ScriptFrameIter iter(cx);
StackFrame *fp = NULL;
Rooted<JSScript*> script(cx);
do {
fp = iter.interpFrame();
script = iter.script();
if (script->needsArgsObj()) {
ArgumentsObject *argsObj;
if (fp->hasArgsObj()) {
argsObj = &fp->argsObj();
} else {
argsObj = ArgumentsObject::createExpected(cx, fp);
if (!argsObj) {
resumeMode = JSINTERP_RETHROW;
break;
}
}
// The arguments is a local binding and needsArgsObj does not
// check if it is clobbered. Ensure that the local binding
// restored during bailout before storing the arguments object
// to the slot.
SetFrameArgumentsObject(cx, fp, script, argsObj);
}
++iter;
} while (fp != br->entryfp());
}
if (activation->entryfp() == br->entryfp()) {
// If the bailout entry fp is the same as the activation entryfp, then
// there are no scripted frames below us. In this case, just shortcut
// out with a special return code, and resume interpreting in the
// original Interpret activation.
vp->setMagic(JS_ION_BAILOUT);
js_delete(br);
return resumeMode == JSINTERP_RETHROW ? Interpret_Error : Interpret_Ok;
}
InterpretStatus status = Interpret(cx, br->entryfp(), resumeMode);
JS_ASSERT_IF(resumeMode == JSINTERP_RETHROW, status == Interpret_Error);
if (status == Interpret_OSR) {
// The interpreter currently does not ask to perform inline OSR, so
// this path is unreachable.
JS_NOT_REACHED("invalid");
IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d",
cx->fp()->script()->filename(),
PCToLineNumber(cx->fp()->script(), cx->regs().pc));
// We want to OSR again. We need to avoid the problem where frequent
// bailouts cause recursive nestings of Interpret and EnterIon. The
// interpreter therefore shortcuts out, and now we're responsible for
// completing the OSR inline.
//
// Note that we set runningInIon so that if we re-enter C++ from within
// the inlined OSR, ScriptFrameIter will know to traverse these frames.
StackFrame *fp = cx->fp();
fp->setRunningInIon();
vp->setPrivate(fp);
js_delete(br);
return Interpret_OSR;
}
if (status == Interpret_Ok)
*vp = br->entryfp()->returnValue();
// The BailoutFrameGuard's destructor will ensure that the frame is
// removed.
js_delete(br);
return status;
}
bool
ion::CheckFrequentBailouts(JSContext *cx, JSScript *script)
{

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