mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to birch
This commit is contained in:
commit
32a7a11a0c
@ -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>
|
26
addon-sdk/source/test/addons/privileged-panel/main.js
Normal file
26
addon-sdk/source/test/addons/privileged-panel/main.js
Normal file
@ -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(©Chunk);
|
||||
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);
|
||||
/**
|
||||
|
12
content/media/test/crashtests/875596.html
Normal file
12
content/media/test/crashtests/875596.html
Normal file
@ -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>
|
3
content/media/test/crashtests/875911.html
Normal file
3
content/media/test/crashtests/875911.html
Normal file
@ -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, ¤tInSampleRate, ¤tOutSampleRate);
|
||||
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, ¤tPosition, mOffset + t, mLoopEnd);
|
||||
CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mOffset + t, mLoopEnd);
|
||||
} else {
|
||||
uint32_t offsetInLoop = (mOffset + t - mLoopEnd) % (mLoopEnd - mLoopStart);
|
||||
CopyFromBuffer(aOutput, channels, &written, ¤tPosition, mLoopStart + offsetInLoop, mLoopEnd);
|
||||
CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mLoopStart + offsetInLoop, mLoopEnd);
|
||||
}
|
||||
} else {
|
||||
if (mOffset + t < mDuration) {
|
||||
CopyFromBuffer(aOutput, channels, &written, ¤tPosition, mOffset + t, mDuration);
|
||||
CopyFromBuffer(aStream, aOutput, channels, &written, ¤tPosition, mOffset + t, mDuration);
|
||||
} else {
|
||||
FillWithZeroes(aOutput, channels, &written, ¤tPosition, 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>
|
46
content/media/webaudio/test/test_bug875402.html
Normal file
46
content/media/webaudio/test/test_bug875402.html
Normal file
@ -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();
|
||||
|
22
dom/webidl/TimeEvent.webidl
Normal file
22
dom/webidl/TimeEvent.webidl
Normal file
@ -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 ®s = 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 ®s = 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)
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user