mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:25:44 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1183915 - Put images dragged from content processes in the drag data in the parent. r=smaug (72906fab07) - Bug 1192394 - Force an image load whenever the thumbnail file changes. r=adw (c194d868f9) - let-var (f5e9a53170) - Bug 1250109 - Change DOMEventTargetHelper subclasses to not assume that GetOwner() is non-null, since it can be nulled out by navigation. r=bzbarsky (deb440c2a4) - Bug 911216 - Part 1: Add tests directly testing content Promise constructor resolved with chrome Promise. r=bz (1b2f1ec6b8) - Bug 911216 - Part 2: Add self-hosting intrinsic for calling wrapped functions without wrapper security checks. r=efaust,bholley (de086e8422) - Bug 911216 - Part 3: Allow wrapped self-hosted functions and intrinsics in the callFunction debug check. r=efaust (c02e6337fe) - Bug 1251921 - Do not call debugger hooks with half-initialized frame if InterpeterFrame::prologue fails. (r=jorendorff) (9873720345) - Bug 1256342. Fix typed array iteration to work correctly over Xrays. r=till (6a7f5c12c6) - Bug 1256376. Fix forEach on typed arrays to work over Xrays from web extension sandboxes. r=till (ab19703ab5) - Bug 911216 - Part 4: Add self-hosting intrinsic for creating arrays in other compartments. r=efaust (37b14521fb) - Bug 1233497 - Temporarily allow unsafe CPOWs in Promise-backend.js and Task.jsm. r=billm (d2672a456a) - Bug 1225041 - Implement ES6 Annex B.3.5 for direct eval. (r=jorendorff) (daf24f0e34) - Bug 1254185 - Deal with missing arguments assigned to block bindings. (r=jimb) (3ce53dcd06) - Bug 1250506 - check if node is acceptable as a child before creating an accessible for it, r=davidb (5960ba726d) - Bug 1251941 - aria::GetRoleMap should take element, r=davidb (e9ee4e20ea) - Bug 1251944 - get rid of nsCoreUtils::GetRoleContent, r=davidb (a2bf199bb4) - Bug 1257030 - Add support for supplying preexisting stack instead of capturing one for use as the async parent stack of CallbackObject. r=bz,tromey (a4ddb41fac) - Bug 1232291 - Non-used header in MessagePortService.*, r=smaug (1e2398e314) - Bug 1255655 - Const-ify sWAIRoleMaps. r=tbsaunde. (09653e44af) - align (24667f7952) - Bug 1253438 - Expose Push observer notification topics. r=markh (b62a068d4b)
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@@ -34,7 +35,7 @@ static const uint32_t kGenericAccType = 0;
|
||||
* via the object attribute "xml-roles".
|
||||
*/
|
||||
|
||||
static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
static const nsRoleMapEntry sWAIRoleMaps[] =
|
||||
{
|
||||
{ // alert
|
||||
&nsGkAtoms::alert,
|
||||
@@ -759,7 +760,7 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
}
|
||||
};
|
||||
|
||||
static nsRoleMapEntry sLandmarkRoleMap = {
|
||||
static const nsRoleMapEntry sLandmarkRoleMap = {
|
||||
&nsGkAtoms::_empty,
|
||||
roles::NOTHING,
|
||||
kUseNativeRole,
|
||||
@@ -860,13 +861,11 @@ struct RoleComparator
|
||||
|
||||
}
|
||||
|
||||
nsRoleMapEntry*
|
||||
aria::GetRoleMap(nsINode* aNode)
|
||||
const nsRoleMapEntry*
|
||||
aria::GetRoleMap(dom::Element* aEl)
|
||||
{
|
||||
nsIContent* content = nsCoreUtils::GetRoleContent(aNode);
|
||||
nsAutoString roles;
|
||||
if (!content ||
|
||||
!content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
|
||||
if (!aEl || !aEl->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
|
||||
roles.IsEmpty()) {
|
||||
// We treat role="" as if the role attribute is absent (per aria spec:8.1.1)
|
||||
return nullptr;
|
||||
|
||||
@@ -210,7 +210,7 @@ extern nsRoleMapEntry gEmptyRoleMap;
|
||||
* @return a pointer to the role map entry for the ARIA role, or nullptr
|
||||
* if none
|
||||
*/
|
||||
nsRoleMapEntry* GetRoleMap(nsINode* aNode);
|
||||
const nsRoleMapEntry* GetRoleMap(dom::Element* aEl);
|
||||
|
||||
/**
|
||||
* Return accessible state from ARIA universal states applied to the given
|
||||
|
||||
@@ -167,8 +167,12 @@ LogDocState(nsIDocument* aDocumentNode)
|
||||
printf(", %svisible considering ancestors", aDocumentNode->IsVisibleConsideringAncestors() ? "" : "not ");
|
||||
printf(", %sactive", aDocumentNode->IsActive() ? "" : "not ");
|
||||
printf(", %sresource", aDocumentNode->IsResourceDoc() ? "" : "not ");
|
||||
printf(", has %srole content",
|
||||
nsCoreUtils::GetRoleContent(aDocumentNode) ? "" : "no ");
|
||||
|
||||
dom::Element* rootEl = aDocumentNode->GetBodyElement();
|
||||
if (!rootEl) {
|
||||
rootEl = aDocumentNode->GetRootElement();
|
||||
}
|
||||
printf(", has %srole content", rootEl ? "" : "no ");
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -107,9 +107,14 @@ TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible,
|
||||
*aSkipSubtree = false;
|
||||
|
||||
if (childEl) {
|
||||
Accessible* accessible = mFlags & eWalkCache ?
|
||||
mDoc->GetAccessible(childEl) :
|
||||
GetAccService()->GetOrCreateAccessible(childEl, mContext, aSkipSubtree);
|
||||
Accessible* accessible = nullptr;
|
||||
if (mFlags & eWalkCache) {
|
||||
accessible = mDoc->GetAccessible(childEl);
|
||||
}
|
||||
else if (mContext->IsAcceptableChild(childEl)) {
|
||||
accessible = GetAccService()->
|
||||
GetOrCreateAccessible(childEl, mContext, aSkipSubtree);
|
||||
}
|
||||
|
||||
// Ignore the accessible and its subtree if it was repositioned by means of
|
||||
// aria-owns.
|
||||
|
||||
@@ -131,11 +131,11 @@ nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
|
||||
|
||||
void
|
||||
nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
nsIContent *aStartContent,
|
||||
nsIContent *aTopContent)
|
||||
nsIContent* aStartContent,
|
||||
dom::Element* aTopEl)
|
||||
{
|
||||
nsAutoString live, relevant, busy;
|
||||
nsIContent *ancestor = aStartContent;
|
||||
nsIContent* ancestor = aStartContent;
|
||||
while (ancestor) {
|
||||
|
||||
// container-relevant attribute
|
||||
@@ -146,10 +146,12 @@ nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
|
||||
// container-live, and container-live-role attributes
|
||||
if (live.IsEmpty()) {
|
||||
nsRoleMapEntry* role = aria::GetRoleMap(ancestor);
|
||||
const nsRoleMapEntry* role = nullptr;
|
||||
if (ancestor->IsElement()) {
|
||||
role = aria::GetRoleMap(ancestor->AsElement());
|
||||
}
|
||||
if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live,
|
||||
live);
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
|
||||
} else if (role) {
|
||||
GetLiveAttrValue(role->liveAttRule, live);
|
||||
}
|
||||
@@ -175,12 +177,12 @@ nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
|
||||
|
||||
if (ancestor == aTopContent)
|
||||
if (ancestor == aTopEl)
|
||||
break;
|
||||
|
||||
ancestor = ancestor->GetParent();
|
||||
if (!ancestor)
|
||||
ancestor = aTopContent; // Use <body>/<frameset>
|
||||
ancestor = aTopEl; // Use <body>/<frameset>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ public:
|
||||
* @param aTopContent node to end at
|
||||
*/
|
||||
static void SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
nsIContent *aStartContent,
|
||||
nsIContent *aTopContent);
|
||||
nsIContent* aStartContent,
|
||||
mozilla::dom::Element* aTopEl);
|
||||
|
||||
/**
|
||||
* Any ARIA property of type boolean or NMTOKEN is undefined if the ARIA
|
||||
|
||||
@@ -142,7 +142,7 @@ New_HTMLLink(nsIContent* aContent, Accessible* aContext)
|
||||
{
|
||||
// Only some roles truly enjoy life as HTMLLinkAccessibles, for details
|
||||
// see closed bug 494807.
|
||||
nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent);
|
||||
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent->AsElement());
|
||||
if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
|
||||
roleMapEntry->role != roles::LINK) {
|
||||
return new HyperTextAccessibleWrap(aContent, aContext->Document());
|
||||
@@ -1105,9 +1105,6 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
}
|
||||
|
||||
newAcc = CreateAccessibleByFrameType(frame, content, aContext);
|
||||
if (!aContext->IsAcceptableChild(newAcc))
|
||||
return nullptr;
|
||||
|
||||
document->BindToDocument(newAcc, nullptr);
|
||||
newAcc->AsTextLeaf()->SetText(text.mString);
|
||||
return newAcc;
|
||||
@@ -1131,14 +1128,11 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
}
|
||||
|
||||
newAcc = new HyperTextAccessibleWrap(content, document);
|
||||
if (!aContext->IsAcceptableChild(newAcc))
|
||||
return nullptr;
|
||||
|
||||
document->BindToDocument(newAcc, aria::GetRoleMap(aNode));
|
||||
document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
|
||||
return newAcc;
|
||||
}
|
||||
|
||||
nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode);
|
||||
const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
|
||||
|
||||
// If the element is focusable or global ARIA attribute is applied to it or
|
||||
// it is referenced by ARIA relationship then treat role="presentation" on
|
||||
@@ -1190,7 +1184,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
// expose their native roles.
|
||||
if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
|
||||
if (frame->AccessibleType() == eHTMLTableRowType) {
|
||||
nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
|
||||
const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
|
||||
if (!contextRoleMap->IsOfType(eTable))
|
||||
roleMapEntry = &aria::gEmptyRoleMap;
|
||||
|
||||
@@ -1202,7 +1196,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
nsGkAtoms::li,
|
||||
nsGkAtoms::dd) ||
|
||||
frame->AccessibleType() == eHTMLLiType) {
|
||||
nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
|
||||
const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
|
||||
if (!contextRoleMap->IsOfType(eList))
|
||||
roleMapEntry = &aria::gEmptyRoleMap;
|
||||
}
|
||||
@@ -1291,10 +1285,9 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
|
||||
}
|
||||
}
|
||||
|
||||
if (!newAcc || !aContext->IsAcceptableChild(newAcc))
|
||||
return nullptr;
|
||||
|
||||
document->BindToDocument(newAcc, roleMapEntry);
|
||||
if (newAcc) {
|
||||
document->BindToDocument(newAcc, roleMapEntry);
|
||||
}
|
||||
return newAcc;
|
||||
}
|
||||
|
||||
@@ -1414,13 +1407,7 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
|
||||
DocAccessible* aDoc)
|
||||
{
|
||||
nsAutoString role;
|
||||
for (const nsXBLBinding* binding = aContent->GetXBLBinding(); binding; binding = binding->GetBaseBinding()) {
|
||||
nsIContent* bindingElm = binding->PrototypeBinding()->GetBindingElement();
|
||||
bindingElm->GetAttr(kNameSpaceID_None, nsGkAtoms::role, role);
|
||||
if (!role.IsEmpty())
|
||||
break;
|
||||
}
|
||||
|
||||
nsCoreUtils::XBLBindingRole(aContent, role);
|
||||
if (role.IsEmpty() || role.EqualsLiteral("none"))
|
||||
return nullptr;
|
||||
|
||||
|
||||
@@ -223,28 +223,6 @@ nsCoreUtils::GetDOMNodeFromDOMPoint(nsINode *aNode, uint32_t aOffset)
|
||||
return aNode;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsCoreUtils::GetRoleContent(nsINode *aNode)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
|
||||
if (!content) {
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(aNode));
|
||||
if (htmlDoc) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> bodyElement;
|
||||
htmlDoc->GetBody(getter_AddRefs(bodyElement));
|
||||
content = do_QueryInterface(bodyElement);
|
||||
}
|
||||
else {
|
||||
return doc->GetDocumentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCoreUtils::IsAncestorOf(nsINode *aPossibleAncestorNode,
|
||||
nsINode *aPossibleDescendantNode,
|
||||
@@ -686,3 +664,15 @@ nsCoreUtils::DispatchAccEvent(RefPtr<nsIAccessibleEvent> event)
|
||||
|
||||
obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::XBLBindingRole(const nsIContent* aEl, nsAString& aRole)
|
||||
{
|
||||
for (const nsXBLBinding* binding = aEl->GetXBLBinding(); binding;
|
||||
binding = binding->GetBaseBinding()) {
|
||||
nsIContent* bindingElm = binding->PrototypeBinding()->GetBindingElement();
|
||||
bindingElm->GetAttr(kNameSpaceID_None, nsGkAtoms::role, aRole);
|
||||
if (!aRole.IsEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,17 +108,6 @@ public:
|
||||
*/
|
||||
static nsINode *GetDOMNodeFromDOMPoint(nsINode *aNode, uint32_t aOffset);
|
||||
|
||||
/**
|
||||
* Return the nsIContent* to check for ARIA attributes on -- this may not
|
||||
* always be the DOM node for the accessible. Specifically, for doc
|
||||
* accessibles, it is not the document node, but either the root element or
|
||||
* <body> in HTML.
|
||||
*
|
||||
* @param aNode [in] DOM node for the accessible that may be affected by ARIA
|
||||
* @return the nsIContent which may have ARIA markup
|
||||
*/
|
||||
static nsIContent* GetRoleContent(nsINode *aNode);
|
||||
|
||||
/**
|
||||
* Is the first passed in node an ancestor of the second?
|
||||
* Note: A node is not considered to be the ancestor of itself.
|
||||
@@ -330,6 +319,11 @@ public:
|
||||
* Notify accessible event observers of an event.
|
||||
*/
|
||||
static void DispatchAccEvent(RefPtr<nsIAccessibleEvent> aEvent);
|
||||
|
||||
/**
|
||||
* Return a role attribute on XBL bindings of the element.
|
||||
*/
|
||||
static void XBLBindingRole(const nsIContent* aEl, nsAString& aRole);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/HTMLBodyElement.h"
|
||||
#include "mozilla/dom/TreeWalker.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@@ -1001,7 +1002,7 @@ Accessible::NativeAttributes()
|
||||
break;
|
||||
|
||||
nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
|
||||
nsCoreUtils::GetRoleContent(doc));
|
||||
doc->GetRootElement());
|
||||
|
||||
// Allow ARIA live region markup from outer documents to override
|
||||
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
|
||||
|
||||
@@ -226,7 +226,7 @@ public:
|
||||
/**
|
||||
* Retrun ARIA role map if any.
|
||||
*/
|
||||
nsRoleMapEntry* ARIARoleMap() const { return mRoleMapEntry; }
|
||||
const nsRoleMapEntry* ARIARoleMap() const { return mRoleMapEntry; }
|
||||
|
||||
/**
|
||||
* Return accessible role specified by ARIA (see constants in
|
||||
@@ -372,7 +372,7 @@ public:
|
||||
/**
|
||||
* Set the ARIA role map entry for a new accessible.
|
||||
*/
|
||||
void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
|
||||
void SetRoleMapEntry(const nsRoleMapEntry* aRoleMapEntry)
|
||||
{ mRoleMapEntry = aRoleMapEntry; }
|
||||
|
||||
/**
|
||||
@@ -497,7 +497,8 @@ public:
|
||||
/**
|
||||
* Return true if the accessible is an acceptable child.
|
||||
*/
|
||||
virtual bool IsAcceptableChild(Accessible* aPossibleChild) const { return true; }
|
||||
virtual bool IsAcceptableChild(nsIContent* aEl) const
|
||||
{ return true; }
|
||||
|
||||
/**
|
||||
* Returns text of accessible if accessible has text role otherwise empty
|
||||
@@ -1178,7 +1179,7 @@ protected:
|
||||
/**
|
||||
* Non-null indicates author-supplied role; possibly state & value as well
|
||||
*/
|
||||
nsRoleMapEntry* mRoleMapEntry;
|
||||
const nsRoleMapEntry* mRoleMapEntry;
|
||||
|
||||
private:
|
||||
Accessible() = delete;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "NotificationController.h"
|
||||
#include "States.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsIDocumentInlines.h"
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
@@ -77,6 +78,19 @@ DocAccessible::UpdateText(nsIContent* aTextNode)
|
||||
mNotificationController->ScheduleTextUpdate(aTextNode);
|
||||
}
|
||||
|
||||
inline void
|
||||
DocAccessible::UpdateRootElIfNeeded()
|
||||
{
|
||||
dom::Element* rootEl = mDocumentNode->GetBodyElement();
|
||||
if (!rootEl) {
|
||||
rootEl = mDocumentNode->GetRootElement();
|
||||
}
|
||||
if (rootEl != mContent) {
|
||||
mContent = rootEl;
|
||||
SetRoleMapEntry(aria::GetRoleMap(rootEl));
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
DocAccessible::AddScrollListener()
|
||||
{
|
||||
|
||||
@@ -1278,7 +1278,7 @@ DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const
|
||||
|
||||
void
|
||||
DocAccessible::BindToDocument(Accessible* aAccessible,
|
||||
nsRoleMapEntry* aRoleMapEntry)
|
||||
const nsRoleMapEntry* aRoleMapEntry)
|
||||
{
|
||||
// Put into DOM node cache.
|
||||
if (aAccessible->IsNodeMapEntry())
|
||||
@@ -1488,14 +1488,8 @@ DocAccessible::DoInitialUpdate()
|
||||
|
||||
mLoadState |= eTreeConstructed;
|
||||
|
||||
// The content element may be changed before the initial update and then we
|
||||
// miss the notification (since content tree change notifications are ignored
|
||||
// prior to initial update). Make sure the content element is valid.
|
||||
nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
|
||||
if (contentElm) {
|
||||
mContent = contentElm;
|
||||
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
||||
}
|
||||
// Set up a root element and ARIA role mapping.
|
||||
UpdateRootElIfNeeded();
|
||||
|
||||
// Build initial tree. Since its the initial tree there's no group info to
|
||||
// invalidate.
|
||||
@@ -1728,11 +1722,7 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
||||
|
||||
if (container == this) {
|
||||
// If new root content has been inserted then update it.
|
||||
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
|
||||
if (rootContent != mContent) {
|
||||
mContent = rootContent;
|
||||
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
||||
}
|
||||
UpdateRootElIfNeeded();
|
||||
|
||||
// Continue to update the tree even if we don't have root content.
|
||||
// For example, elements may be inserted under the document element while
|
||||
|
||||
@@ -309,7 +309,8 @@ public:
|
||||
* @param aRoleMapEntry [in] the role map entry role the ARIA role or nullptr
|
||||
* if none
|
||||
*/
|
||||
void BindToDocument(Accessible* aAccessible, nsRoleMapEntry* aRoleMapEntry);
|
||||
void BindToDocument(Accessible* aAccessible,
|
||||
const nsRoleMapEntry* aRoleMapEntry);
|
||||
|
||||
/**
|
||||
* Remove from document and shutdown the given accessible.
|
||||
@@ -379,6 +380,11 @@ protected:
|
||||
*/
|
||||
virtual void DoInitialUpdate();
|
||||
|
||||
/**
|
||||
* Updates root element and picks up ARIA role on it if any.
|
||||
*/
|
||||
void UpdateRootElIfNeeded();
|
||||
|
||||
/**
|
||||
* Process document load notification, fire document load and state busy
|
||||
* events if applicable.
|
||||
|
||||
@@ -109,11 +109,10 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
||||
uint32_t areaElmCount = imageMapObj->AreaCount();
|
||||
for (uint32_t idx = 0; idx < areaElmCount; idx++) {
|
||||
nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
|
||||
|
||||
Accessible* area = mChildren.SafeElementAt(idx);
|
||||
if (!area || area->GetContent() != areaContent) {
|
||||
RefPtr<Accessible> area = new HTMLAreaAccessible(areaContent, mDoc);
|
||||
mDoc->BindToDocument(area, aria::GetRoleMap(areaContent));
|
||||
mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement()));
|
||||
|
||||
if (!InsertChildAt(idx, area)) {
|
||||
mDoc->UnbindFromDocument(area);
|
||||
|
||||
@@ -353,7 +353,7 @@ DocAccessibleChild::RecvARIARoleAtom(const uint64_t& aID, nsString* aRole)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nsRoleMapEntry* roleMap = acc->ARIARoleMap()) {
|
||||
if (const nsRoleMapEntry* roleMap = acc->ARIARoleMap()) {
|
||||
if (nsIAtom* roleAtom = *(roleMap->roleAtom)) {
|
||||
roleAtom->ToString(*aRole);
|
||||
}
|
||||
|
||||
@@ -811,7 +811,7 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
||||
// Now, deal with widget roles
|
||||
nsIAtom* roleAtom = nullptr;
|
||||
if (accWrap && accWrap->HasARIARole()) {
|
||||
nsRoleMapEntry* roleMap = accWrap->ARIARoleMap();
|
||||
const nsRoleMapEntry* roleMap = accWrap->ARIARoleMap();
|
||||
roleAtom = *roleMap->roleAtom;
|
||||
}
|
||||
if (proxy)
|
||||
|
||||
@@ -130,10 +130,14 @@ XULColorPickerAccessible::AreItemsOperable() const
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULColorPickerAccessible: protected Accessible
|
||||
// XULColorPickerAccessible: Accessible
|
||||
|
||||
bool
|
||||
XULColorPickerAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
|
||||
XULColorPickerAccessible::IsAcceptableChild(nsIContent* aEl) const
|
||||
{
|
||||
return roles::ALERT == aPossibleChild->Role();
|
||||
nsAutoString role;
|
||||
nsCoreUtils::XBLBindingRole(aEl, role);
|
||||
return role.EqualsLiteral("xul:panel") &&
|
||||
aEl->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
|
||||
nsGkAtoms::_true, eCaseMatters);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
virtual bool IsActiveWidget() const override;
|
||||
virtual bool AreItemsOperable() const override;
|
||||
|
||||
virtual bool IsAcceptableChild(Accessible* aPossibleChild) const override;
|
||||
virtual bool IsAcceptableChild(nsIContent* aEl) const override;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
||||
@@ -165,7 +165,7 @@ XULButtonAccessible::ContainerWidget() const
|
||||
}
|
||||
|
||||
bool
|
||||
XULButtonAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
|
||||
XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const
|
||||
{
|
||||
// In general XUL button has not accessible children. Nevertheless menu
|
||||
// buttons can have button (@type="menu-button") and popup accessibles
|
||||
@@ -173,17 +173,21 @@ XULButtonAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
|
||||
|
||||
// XXX: no children until the button is menu button. Probably it's not
|
||||
// totally correct but in general AT wants to have leaf buttons.
|
||||
roles::Role role = aPossibleChild->Role();
|
||||
nsAutoString role;
|
||||
nsCoreUtils::XBLBindingRole(aEl, role);
|
||||
|
||||
// Get an accessible for menupopup or panel elements.
|
||||
if (role == roles::MENUPOPUP)
|
||||
if (role.EqualsLiteral("xul:menupopup")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Button type="menu-button" contains a real button. Get an accessible
|
||||
// for it. Ignore dropmarker button which is placed as a last child.
|
||||
if (role != roles::PUSHBUTTON ||
|
||||
aPossibleChild->GetContent()->IsXULElement(nsGkAtoms::dropMarker))
|
||||
if ((!role.EqualsLiteral("xul:button") &&
|
||||
!role.EqualsLiteral("xul:toolbarbutton")) ||
|
||||
aEl->IsXULElement(nsGkAtoms::dropMarker)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::menuButton, eCaseMatters);
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
virtual bool AreItemsOperable() const override;
|
||||
virtual Accessible* ContainerWidget() const override;
|
||||
|
||||
virtual bool IsAcceptableChild(Accessible* aPossibleChild) const override;
|
||||
virtual bool IsAcceptableChild(nsIContent* aEl) const override;
|
||||
|
||||
protected:
|
||||
virtual ~XULButtonAccessible();
|
||||
|
||||
@@ -348,6 +348,11 @@ bool
|
||||
ScreenOrientation::LockDeviceOrientation(ScreenOrientationInternal aOrientation,
|
||||
bool aIsFullScreen, ErrorResult& aRv)
|
||||
{
|
||||
if (!GetOwner()) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc());
|
||||
// We need to register a listener so we learn when we leave full-screen
|
||||
// and when we will have to unlock the screen.
|
||||
@@ -385,7 +390,8 @@ ScreenOrientation::UnlockDeviceOrientation()
|
||||
{
|
||||
hal::UnlockScreenOrientation();
|
||||
|
||||
if (!mFullScreenListener) {
|
||||
if (!mFullScreenListener || !GetOwner()) {
|
||||
mFullScreenListener = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
#include "MediaDecoder.h"
|
||||
// nsNPAPIPluginInstance must be included before nsIDocument.h, which is included in mozAutoDocUpdate.h.
|
||||
#include "nsNPAPIPluginInstance.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "ImageOps.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@@ -200,6 +202,7 @@
|
||||
#include "nsICookieService.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/BloomFilter.h"
|
||||
#include "SourceSurfaceRawData.h"
|
||||
|
||||
#include "nsIBidiKeyboard.h"
|
||||
|
||||
@@ -216,6 +219,7 @@ extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
|
||||
class imgLoader;
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::widget;
|
||||
using namespace mozilla;
|
||||
@@ -7470,6 +7474,48 @@ nsContentUtils::IsFileImage(nsIFile* aFile, nsACString& aType)
|
||||
return StringBeginsWith(aType, NS_LITERAL_CSTRING("image/"));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::DataTransferItemToImage(const IPCDataTransferItem& aItem,
|
||||
imgIContainer** aContainer)
|
||||
{
|
||||
MOZ_ASSERT(aItem.data().type() == IPCDataTransferData::TnsCString);
|
||||
MOZ_ASSERT(IsFlavorImage(aItem.flavor()));
|
||||
|
||||
const IPCDataTransferImage& imageDetails = aItem.imageDetails();
|
||||
const IntSize size(imageDetails.width(), imageDetails.height());
|
||||
if (!size.width || !size.height) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const nsCString& text = aItem.data().get_nsCString();
|
||||
|
||||
// InitWrappingData takes a non-const pointer for reading.
|
||||
nsCString& nonConstText = const_cast<nsCString&>(text);
|
||||
RefPtr<SourceSurfaceRawData> image = new SourceSurfaceRawData();
|
||||
image->InitWrappingData(reinterpret_cast<uint8_t*>(nonConstText.BeginWriting()),
|
||||
size, imageDetails.stride(),
|
||||
static_cast<SurfaceFormat>(imageDetails.format()),
|
||||
false);
|
||||
image->GuaranteePersistance();
|
||||
|
||||
RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
|
||||
nsCOMPtr<imgIContainer> imageContainer =
|
||||
image::ImageOps::CreateFromDrawable(drawable);
|
||||
imageContainer.forget(aContainer);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::IsFlavorImage(const nsACString& aFlavor)
|
||||
{
|
||||
return aFlavor.EqualsLiteral(kNativeImageMime) ||
|
||||
aFlavor.EqualsLiteral(kJPEGImageMime) ||
|
||||
aFlavor.EqualsLiteral(kJPGImageMime) ||
|
||||
aFlavor.EqualsLiteral(kPNGImageMime) ||
|
||||
aFlavor.EqualsLiteral(kGIFImageMime);
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
||||
IPCDataTransfer* aIPCDataTransfer,
|
||||
|
||||
@@ -121,6 +121,7 @@ class DocumentFragment;
|
||||
class Element;
|
||||
class EventTarget;
|
||||
class IPCDataTransfer;
|
||||
class IPCDataTransferItem;
|
||||
class NodeInfo;
|
||||
class nsIContentChild;
|
||||
class nsIContentParent;
|
||||
@@ -2431,6 +2432,21 @@ public:
|
||||
*/
|
||||
static bool IsFileImage(nsIFile* aFile, nsACString& aType);
|
||||
|
||||
/**
|
||||
* Given an IPCDataTransferItem that has a flavor for which IsFlavorImage
|
||||
* returns true and whose IPCDataTransferData is of type nsCString (raw image
|
||||
* data), construct an imgIContainer for the image encoded by the transfer
|
||||
* item.
|
||||
*/
|
||||
static nsresult DataTransferItemToImage(const mozilla::dom::IPCDataTransferItem& aItem,
|
||||
imgIContainer** aContainer);
|
||||
|
||||
/**
|
||||
* Given a flavor obtained from an IPCDataTransferItem or nsITransferable,
|
||||
* returns true if we should treat the data as an image.
|
||||
*/
|
||||
static bool IsFlavorImage(const nsACString& aFlavor);
|
||||
|
||||
static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
|
||||
nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
|
||||
bool aInSyncMessage,
|
||||
|
||||
@@ -778,9 +778,15 @@ nsPerformance::InsertUserEntry(PerformanceEntry* aEntry)
|
||||
|
||||
nsAutoCString uri;
|
||||
uint64_t markCreationEpoch = 0;
|
||||
|
||||
if (nsContentUtils::IsUserTimingLoggingEnabled() ||
|
||||
nsContentUtils::SendPerformanceTimingNotifications()) {
|
||||
nsresult rv = GetOwner()->GetDocumentURI()->GetHost(uri);
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
if (owner && owner->GetDocumentURI()) {
|
||||
rv = owner->GetDocumentURI()->GetHost(uri);
|
||||
}
|
||||
|
||||
if(NS_FAILED(rv)) {
|
||||
// If we have no URI, just put in "none".
|
||||
uri.AssignLiteral("none");
|
||||
|
||||
@@ -134,7 +134,7 @@ BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInf
|
||||
mLevel = aBatteryInfo.level();
|
||||
|
||||
// Round to the nearest ten percent for non-chrome and non-certified apps
|
||||
nsIDocument* doc = GetOwner()->GetDoc();
|
||||
nsIDocument* doc = GetOwner() ? GetOwner()->GetDoc() : nullptr;
|
||||
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
if (doc) {
|
||||
doc->NodePrincipal()->GetAppStatus(&status);
|
||||
|
||||
@@ -32,6 +32,14 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// See CallbackObject for an explanation of the arguments.
|
||||
explicit CallbackFunction(JS::Handle<JSObject*> aCallable,
|
||||
JS::Handle<JSObject*> aAsyncStack,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
: CallbackObject(aCallable, aAsyncStack, aIncumbentGlobal)
|
||||
{
|
||||
}
|
||||
|
||||
JS::Handle<JSObject*> Callable() const
|
||||
{
|
||||
return Callback();
|
||||
|
||||
@@ -31,6 +31,14 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// See CallbackObject for an explanation of the arguments.
|
||||
explicit CallbackInterface(JS::Handle<JSObject*> aCallback,
|
||||
JS::Handle<JSObject*> aAsyncStack,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
: CallbackObject(aCallback, aAsyncStack, aIncumbentGlobal)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
|
||||
JS::MutableHandle<JS::Value> aCallable);
|
||||
|
||||
@@ -67,6 +67,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Instead of capturing the current stack to use as an async parent when the
|
||||
// callback is invoked, the caller can use this overload to pass in a stack
|
||||
// for that purpose.
|
||||
explicit CallbackObject(JS::Handle<JSObject*> aCallback,
|
||||
JS::Handle<JSObject*> aAsyncStack,
|
||||
nsIGlobalObject *aIncumbentGlobal)
|
||||
{
|
||||
Init(aCallback, aAsyncStack, aIncumbentGlobal);
|
||||
}
|
||||
|
||||
JS::Handle<JSObject*> Callback() const
|
||||
{
|
||||
JS::ExposeObjectToActiveJS(mCallback);
|
||||
|
||||
+23
-11
@@ -14672,17 +14672,29 @@ class CGCallback(CGClass):
|
||||
# Not much we can assert about it, other than not being null, and
|
||||
# CallbackObject does that already.
|
||||
body = ""
|
||||
return [ClassConstructor(
|
||||
[Argument("JSContext*", "aCx"),
|
||||
Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("nsIGlobalObject*", "aIncumbentGlobal")],
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
explicit=True,
|
||||
baseConstructors=[
|
||||
"%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName,
|
||||
],
|
||||
body=body)]
|
||||
return [
|
||||
ClassConstructor(
|
||||
[Argument("JSContext*", "aCx"),
|
||||
Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("nsIGlobalObject*", "aIncumbentGlobal")],
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
explicit=True,
|
||||
baseConstructors=[
|
||||
"%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName,
|
||||
],
|
||||
body=body),
|
||||
ClassConstructor(
|
||||
[Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("JS::Handle<JSObject*>", "aAsyncStack"),
|
||||
Argument("nsIGlobalObject*", "aIncumbentGlobal")],
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
explicit=True,
|
||||
baseConstructors=[
|
||||
"%s(aCallback, aAsyncStack, aIncumbentGlobal)" % self.baseName,
|
||||
],
|
||||
body=body)]
|
||||
|
||||
def getMethodImpls(self, method):
|
||||
assert method.needThisHandling
|
||||
|
||||
@@ -73,6 +73,10 @@ interface nsIPushClearResultCallback : nsISupports
|
||||
[scriptable, uuid(678ef584-bf25-47aa-ac84-03efc0865b68)]
|
||||
interface nsIPushService : nsISupports
|
||||
{
|
||||
/** Observer topic names, exported for convenience. */
|
||||
readonly attribute DOMString pushTopic;
|
||||
readonly attribute DOMString subscriptionChangeTopic;
|
||||
|
||||
/**
|
||||
* Creates a push subscription for the given |scope| URL and |principal|.
|
||||
* If a subscription already exists for this |(scope, principal)| pair,
|
||||
|
||||
+36
-57
@@ -153,8 +153,6 @@
|
||||
#include "nsPIWindowWatcher.h"
|
||||
#include "nsWindowWatcher.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "gfxDrawable.h"
|
||||
#include "ImageOps.h"
|
||||
#include "mozilla/dom/nsMixedContentBlocker.h"
|
||||
#include "nsMemoryInfoDumper.h"
|
||||
#include "nsMemoryReporterManager.h"
|
||||
@@ -2746,75 +2744,56 @@ ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
||||
nsCOMPtr<nsITransferable> trans =
|
||||
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
trans->Init(nullptr);
|
||||
|
||||
const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
|
||||
for (uint32_t j = 0; j < items.Length(); ++j) {
|
||||
const IPCDataTransferItem& item = items[j];
|
||||
|
||||
for (const auto& item : items) {
|
||||
trans->AddDataFlavor(item.flavor().get());
|
||||
|
||||
if (item.data().type() == IPCDataTransferData::TnsString) {
|
||||
nsCOMPtr<nsISupportsString> dataWrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
nsString text = item.data().get_nsString();
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
rv = trans->SetTransferData(item.flavor().get(), dataWrapper,
|
||||
text.Length() * sizeof(char16_t));
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
} else if (item.data().type() == IPCDataTransferData::TnsCString) {
|
||||
if (item.flavor().EqualsLiteral(kNativeImageMime) ||
|
||||
item.flavor().EqualsLiteral(kJPEGImageMime) ||
|
||||
item.flavor().EqualsLiteral(kJPGImageMime) ||
|
||||
item.flavor().EqualsLiteral(kPNGImageMime) ||
|
||||
item.flavor().EqualsLiteral(kGIFImageMime)) {
|
||||
const IPCDataTransferImage& imageDetails = item.imageDetails();
|
||||
const gfx::IntSize size(imageDetails.width(), imageDetails.height());
|
||||
if (!size.width || !size.height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCString text = item.data().get_nsCString();
|
||||
RefPtr<gfx::DataSourceSurface> image =
|
||||
new mozilla::gfx::SourceSurfaceRawData();
|
||||
mozilla::gfx::SourceSurfaceRawData* raw =
|
||||
static_cast<mozilla::gfx::SourceSurfaceRawData*>(image.get());
|
||||
raw->InitWrappingData(
|
||||
reinterpret_cast<uint8_t*>(const_cast<nsCString&>(text).BeginWriting()),
|
||||
size, imageDetails.stride(),
|
||||
static_cast<mozilla::gfx::SurfaceFormat>(imageDetails.format()), false);
|
||||
raw->GuaranteePersistance();
|
||||
|
||||
RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
|
||||
nsCOMPtr<imgIContainer> imageContainer(image::ImageOps::CreateFromDrawable(drawable));
|
||||
|
||||
nsCOMPtr<nsISupportsInterfacePointer>
|
||||
imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
|
||||
|
||||
rv = imgPtr->SetData(imageContainer);
|
||||
nsCOMPtr<nsISupportsString> dataWrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
|
||||
} else {
|
||||
nsCOMPtr<nsISupportsCString> dataWrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
const nsCString& text = item.data().get_nsCString();
|
||||
const nsString& text = item.data().get_nsString();
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
rv = trans->SetTransferData(item.flavor().get(), dataWrapper, text.Length());
|
||||
rv = trans->SetTransferData(item.flavor().get(), dataWrapper,
|
||||
text.Length() * sizeof(char16_t));
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
}
|
||||
} else if (item.data().type() == IPCDataTransferData::TnsCString) {
|
||||
if (nsContentUtils::IsFlavorImage(item.flavor())) {
|
||||
nsCOMPtr<imgIContainer> imageContainer;
|
||||
rv = nsContentUtils::DataTransferItemToImage(item,
|
||||
getter_AddRefs(imageContainer));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
nsCOMPtr<nsISupportsInterfacePointer> imgPtr =
|
||||
do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(imgPtr, true);
|
||||
|
||||
rv = imgPtr->SetData(imageContainer);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
|
||||
} else {
|
||||
nsCOMPtr<nsISupportsCString> dataWrapper =
|
||||
do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
const nsCString& text = item.data().get_nsCString();
|
||||
rv = dataWrapper->SetData(text);
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
rv = trans->SetTransferData(item.flavor().get(), dataWrapper, text.Length());
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-27
@@ -3131,24 +3131,7 @@ TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
|
||||
|
||||
EventStateManager* esm = shell->GetPresContext()->EventStateManager();
|
||||
for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
|
||||
auto& items = aTransfers[i].items();
|
||||
nsTArray<DataTransferItem>* itemArray = mInitialDataTransferItems.AppendElement();
|
||||
for (uint32_t j = 0; j < items.Length(); ++j) {
|
||||
const IPCDataTransferItem& item = items[j];
|
||||
DataTransferItem* localItem = itemArray->AppendElement();
|
||||
localItem->mFlavor = item.flavor();
|
||||
if (item.data().type() == IPCDataTransferData::TnsString) {
|
||||
localItem->mType = DataTransferItem::DataType::eString;
|
||||
localItem->mStringData = item.data().get_nsString();
|
||||
} else if (item.data().type() == IPCDataTransferData::TPBlobChild) {
|
||||
localItem->mType = DataTransferItem::DataType::eBlob;
|
||||
BlobParent* blobParent =
|
||||
static_cast<BlobParent*>(item.data().get_PBlobParent());
|
||||
if (blobParent) {
|
||||
localItem->mBlobData = blobParent->GetBlobImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
mInitialDataTransferItems.AppendElement(mozilla::Move(aTransfers[i].items()));
|
||||
}
|
||||
if (Manager()->IsContentParent()) {
|
||||
nsCOMPtr<nsIDragService> dragService =
|
||||
@@ -3184,24 +3167,37 @@ void
|
||||
TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
|
||||
{
|
||||
for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) {
|
||||
nsTArray<DataTransferItem>& itemArray = mInitialDataTransferItems[i];
|
||||
for (uint32_t j = 0; j < itemArray.Length(); ++j) {
|
||||
DataTransferItem& item = itemArray[j];
|
||||
nsTArray<IPCDataTransferItem>& itemArray = mInitialDataTransferItems[i];
|
||||
for (auto& item : itemArray) {
|
||||
RefPtr<nsVariantCC> variant = new nsVariantCC();
|
||||
// Special case kFilePromiseMime so that we get the right
|
||||
// nsIFlavorDataProvider for it.
|
||||
if (item.mFlavor.EqualsLiteral(kFilePromiseMime)) {
|
||||
if (item.flavor().EqualsLiteral(kFilePromiseMime)) {
|
||||
RefPtr<nsISupports> flavorDataProvider =
|
||||
new nsContentAreaDragDropDataProvider();
|
||||
variant->SetAsISupports(flavorDataProvider);
|
||||
} else if (item.mType == DataTransferItem::DataType::eString) {
|
||||
variant->SetAsAString(item.mStringData);
|
||||
} else if (item.mType == DataTransferItem::DataType::eBlob) {
|
||||
variant->SetAsISupports(item.mBlobData);
|
||||
} else if (item.data().type() == IPCDataTransferData::TnsString) {
|
||||
variant->SetAsAString(item.data().get_nsString());
|
||||
} else if (item.data().type() == IPCDataTransferData::TPBlobParent) {
|
||||
auto* parent = static_cast<BlobParent*>(item.data().get_PBlobParent());
|
||||
RefPtr<BlobImpl> impl = parent->GetBlobImpl();
|
||||
variant->SetAsISupports(impl);
|
||||
} else if (item.data().type() == IPCDataTransferData::TnsCString &&
|
||||
nsContentUtils::IsFlavorImage(item.flavor())) {
|
||||
// An image! Get the imgIContainer for it and set it in the variant.
|
||||
nsCOMPtr<imgIContainer> imageContainer;
|
||||
nsresult rv =
|
||||
nsContentUtils::DataTransferItemToImage(item,
|
||||
getter_AddRefs(imageContainer));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
variant->SetAsISupports(imageContainer);
|
||||
}
|
||||
|
||||
// Using system principal here, since once the data is on parent process
|
||||
// side, it can be handled as being from browser chrome or OS.
|
||||
aDataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.mFlavor),
|
||||
aDataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.flavor()),
|
||||
variant, i,
|
||||
nsContentUtils::GetSystemPrincipal());
|
||||
}
|
||||
|
||||
+1
-13
@@ -647,19 +647,7 @@ private:
|
||||
|
||||
uint32_t mChromeFlags;
|
||||
|
||||
struct DataTransferItem
|
||||
{
|
||||
nsCString mFlavor;
|
||||
nsString mStringData;
|
||||
RefPtr<mozilla::dom::BlobImpl> mBlobData;
|
||||
enum DataType
|
||||
{
|
||||
eString,
|
||||
eBlob
|
||||
};
|
||||
DataType mType;
|
||||
};
|
||||
nsTArray<nsTArray<DataTransferItem>> mInitialDataTransferItems;
|
||||
nsTArray<nsTArray<IPCDataTransferItem>> mInitialDataTransferItems;
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> mDnDVisualization;
|
||||
int32_t mDragAreaX;
|
||||
|
||||
@@ -557,6 +557,10 @@ private:
|
||||
|
||||
bool CheckPermission(const char* aType)
|
||||
{
|
||||
if (!mRecorder || !mRecorder->GetOwner()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
@@ -1158,7 +1162,7 @@ MediaRecorder::GetSourcePrincipal()
|
||||
return mDOMStream->GetPrincipal();
|
||||
}
|
||||
MOZ_ASSERT(mAudioNode != nullptr);
|
||||
nsIDocument* doc = mAudioNode->GetOwner()->GetExtantDoc();
|
||||
nsIDocument* doc = mAudioNode->GetOwner() ? mAudioNode->GetOwner()->GetExtantDoc() : nullptr;
|
||||
return doc ? doc->NodePrincipal() : nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
bool mCanSendData;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_MessagePortParent_h
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
using mozilla::ipc::AssertIsOnBackgroundThread;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
iframe_messageChannel_chrome.html
|
||||
mm_messageChannelParent.xul
|
||||
mm_messageChannelParentNotRemote.xul
|
||||
mm_messageChannelParent.js
|
||||
mm_messageChannel.js
|
||||
|
||||
[test_messageChannel.xul]
|
||||
[test_messageChannelWithMessageManager.xul]
|
||||
skip-if = os == 'android'
|
||||
[test_messageChannelWithMessageManagerNotRemote.xul]
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
function debug(msg) {
|
||||
dump("[mmMessageChannelChild]" + msg + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Preparation Test
|
||||
*/
|
||||
let port;
|
||||
let toString = Object.prototype.toString;
|
||||
|
||||
(function prepare() {
|
||||
debug("Script loaded.");
|
||||
addTestReceiver();
|
||||
sendAsyncMessage("mmMessagePort:finishScriptLoad", {}, {});
|
||||
})();
|
||||
|
||||
function ok(condition, message) {
|
||||
debug('condition: ' + condition + ', ' + message + '\n');
|
||||
if (!condition) {
|
||||
sendAsyncMessage("mmMessagePort:fail", { message: message });
|
||||
throw 'failed check: ' + message;
|
||||
}
|
||||
}
|
||||
|
||||
function is(a, b, message) {
|
||||
ok(a===b, message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Testing codes.
|
||||
*/
|
||||
function addTestReceiver() {
|
||||
addMessageListener("BasicTest:PortCreated", basicTest);
|
||||
addMessageListener("CloseTest:PortCreated", closeTest);
|
||||
addMessageListener("EmptyTest:PortCreated", emptyTest);
|
||||
addMessageListener("NotTransferableTest:PortCreated", notTransferableTest);
|
||||
}
|
||||
|
||||
function basicTest(msg) {
|
||||
port = msg.ports[0];
|
||||
is(toString.call(port), "[object MessagePort]", "created MessagePort.");
|
||||
|
||||
port.onmessage = (msg) => {
|
||||
is(msg.data, "BasicTest:StartTest", "Replied message is correct.");
|
||||
port.postMessage("BasicTest:TestOK");
|
||||
};
|
||||
|
||||
sendAsyncMessage("BasicTest:FinishPrepare", { message: "OK" });
|
||||
}
|
||||
|
||||
function closeTest(msg) {
|
||||
port = msg.ports[0];
|
||||
is(toString.call(port), "[object MessagePort]", "created MessagePort.");
|
||||
|
||||
port.onmessage = (msg) => {
|
||||
ok(msg.data,"CloseTest:StartTest", "Replied message is correct.");
|
||||
port.postMessage("CloseTest:TestOK");
|
||||
};
|
||||
|
||||
port.close();
|
||||
|
||||
sendAsyncMessage("CloseTest:FinishPrepare", { message: "OK"});
|
||||
}
|
||||
|
||||
function emptyTest(msg) {
|
||||
let portSize = msg.ports.length;
|
||||
is(portSize, 0, "transfered port size is zero.");
|
||||
|
||||
sendAsyncMessage("EmptyTest:FinishPrepare", { message: "OK"});
|
||||
}
|
||||
|
||||
function notTransferableTest(msg) {
|
||||
sendAsyncMessage("NotTransferableTest:FinishPrepare", {message: "OK"});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
let port;
|
||||
let mm;
|
||||
|
||||
function info(message) {
|
||||
return opener.wrappedJSObject.info(message);
|
||||
}
|
||||
|
||||
function ok(condition, message) {
|
||||
return opener.wrappedJSObject.ok(condition, message);
|
||||
}
|
||||
|
||||
function is(v1, v2, message) {
|
||||
return opener.wrappedJSObject.is(v1, v2, message);
|
||||
}
|
||||
|
||||
function todo_is(v1, v2, message) {
|
||||
return opener.wrappedJSObject.todo_is(v1, v2, message);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
opener.setTimeout("done()", 0);
|
||||
window.close();
|
||||
}
|
||||
|
||||
function debug(msg) {
|
||||
dump("[mmMessageChannelParent]" + msg + "\n");
|
||||
}
|
||||
|
||||
let tests = [ basic_test,
|
||||
close_test,
|
||||
empty_transferable,
|
||||
not_transferable];
|
||||
|
||||
// Test Routine
|
||||
function run_tests() {
|
||||
let test = tests.shift();
|
||||
if (test === undefined) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
setTimeout(run_tests,0);
|
||||
});
|
||||
}
|
||||
|
||||
// Basic communication test.
|
||||
function basic_test(finish) {
|
||||
ok(mm, "basic_test");
|
||||
|
||||
let finishPrepare = (msg) => {
|
||||
is(msg.data.message, "OK", "");
|
||||
ok(port, "");
|
||||
port.onmessage = (msg) => {
|
||||
is(msg.data, "BasicTest:TestOK", "");
|
||||
finish();
|
||||
}
|
||||
port.postMessage("BasicTest:StartTest");
|
||||
mm.removeMessageListener("BasicTest:FinishPrepare", finishPrepare);
|
||||
};
|
||||
|
||||
let channel = new MessageChannel();
|
||||
port = channel.port2;
|
||||
mm.addMessageListener("BasicTest:FinishPrepare", finishPrepare);
|
||||
mm.sendAsyncMessage("BasicTest:PortCreated", {}, {}, undefined, [channel.port1]);
|
||||
}
|
||||
|
||||
// Communicate with closed port.
|
||||
function close_test(finish) {
|
||||
ok(mm, "close_test");
|
||||
|
||||
let finishPrepare = (msg) => {
|
||||
is(msg.data.message, "OK", "");
|
||||
ok(port, "");
|
||||
|
||||
port.onmessage = (msg) => {
|
||||
ok(false, "Port is alive.");
|
||||
finish();
|
||||
}
|
||||
|
||||
port.postMessage("CloseTest:StartTest");
|
||||
mm.removeMessageListener("CloseTest:FinishPrepare", finishPrepare);
|
||||
finish();
|
||||
}
|
||||
|
||||
let channel = new MessageChannel();
|
||||
port = channel.port2;
|
||||
mm.addMessageListener("CloseTest:FinishPrepare", finishPrepare);
|
||||
mm.sendAsyncMessage("CloseTest:PortCreated", {}, {}, undefined, [channel.port1]);
|
||||
}
|
||||
|
||||
// Empty transferable object
|
||||
function empty_transferable(finish) {
|
||||
ok(mm, "empty_transferable");
|
||||
|
||||
let finishPrepare = (msg) => {
|
||||
ok(true, "Same basic test.");
|
||||
mm.removeMessageListener("EmptyTest:FinishPrepare", finishPrepare);
|
||||
finish();
|
||||
};
|
||||
|
||||
mm.addMessageListener("EmptyTest:FinishPrepare", finishPrepare);
|
||||
mm.sendAsyncMessage("EmptyTest:PortCreated", {}, {}, undefined, []);
|
||||
}
|
||||
|
||||
// Not transferable object.
|
||||
function not_transferable(finish) {
|
||||
ok(mm, "not_transferable");
|
||||
|
||||
let finishPrepare = (msg) => {
|
||||
ok(true, "Same basic test.");
|
||||
finish();
|
||||
}
|
||||
|
||||
mm.addMessageListener("NotTransferableTest:FinishPrepare", finishPrepare);
|
||||
mm.sendAsyncMessage("NotTransferableTest:PortCreated", {}, {}, undefined, [""]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test preparation
|
||||
*/
|
||||
function finishLoad(msg) {
|
||||
run_tests();
|
||||
}
|
||||
|
||||
function prepare_test() {
|
||||
debug("start run_tests()");
|
||||
var node = document.getElementById('messagechannel_remote');
|
||||
mm = node.messageManager; //Services.ppmm.getChildAt(1);
|
||||
ok(mm, "created MessageManager.")
|
||||
|
||||
mm.addMessageListener("mmMessagePort:finishScriptLoad", finishLoad);
|
||||
mm.addMessageListener("mmMessagePort:fail", failed_test);
|
||||
//mm.loadProcessScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
|
||||
mm.loadFrameScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
|
||||
ok(true, "Loaded");
|
||||
}
|
||||
|
||||
function failed_test() {
|
||||
debug("failed test in child process");
|
||||
ok(false, "");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="prepare_test()">
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
|
||||
<browser type="content" src="about:blank" id="messagechannel_remote" remote="true"/>
|
||||
</window>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="prepare_test()">
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
|
||||
<browser type="content" src="about:blank" id="messagechannel_remote"/>
|
||||
</window>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function done() {
|
||||
info("done called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
addLoadEvent(function() {
|
||||
window.open("mm_messageChannelParent.xul", "", "chrome");
|
||||
});
|
||||
]]></script>
|
||||
</window>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function done() {
|
||||
info("done called");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
addLoadEvent(function() {
|
||||
window.open("mm_messageChannelParentNotRemote.xul", "", "chrome");
|
||||
});
|
||||
]]></script>
|
||||
</window>
|
||||
@@ -32,7 +32,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1178076
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
|
||||
runTest();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -78,7 +78,8 @@ DesktopNotification::PostDesktopNotification()
|
||||
do_GetService("@mozilla.org/system-alerts-service;1");
|
||||
if (appNotifier) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
|
||||
uint32_t appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
|
||||
uint32_t appId = window ? window->GetDoc()->NodePrincipal()->GetAppId()
|
||||
: nsIScriptSecurityManager::UNKNOWN_APP_ID;
|
||||
|
||||
if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
|
||||
@@ -111,7 +112,11 @@ DesktopNotification::PostDesktopNotification()
|
||||
// to nsIObservers, thus cookies must be unique to differentiate observers.
|
||||
nsString uniqueName = NS_LITERAL_STRING("desktop-notification:");
|
||||
uniqueName.AppendInt(sCount++);
|
||||
nsCOMPtr<nsIDocument> doc = GetOwner()->GetDoc();
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
if (!owner) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIDocument> doc = owner->GetDoc();
|
||||
nsIPrincipal* principal = doc->NodePrincipal();
|
||||
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
|
||||
bool inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
|
||||
|
||||
@@ -1659,8 +1659,16 @@ ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
|
||||
bool
|
||||
Notification::IsInPrivateBrowsing()
|
||||
{
|
||||
nsIDocument* doc = mWorkerPrivate ? mWorkerPrivate->GetDocument()
|
||||
: GetOwner()->GetExtantDoc();
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsIDocument* doc = nullptr;
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
doc = mWorkerPrivate->GetDocument();
|
||||
} else if (GetOwner()) {
|
||||
doc = GetOwner()->GetExtantDoc();
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
|
||||
return loadContext && loadContext->UsePrivateBrowsing();
|
||||
@@ -1766,7 +1774,9 @@ Notification::ShowInternal()
|
||||
appId = mWorkerPrivate->GetPrincipal()->GetAppId();
|
||||
} else {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetOwner();
|
||||
appId = (window.get())->GetDoc()->NodePrincipal()->GetAppId();
|
||||
if (window) {
|
||||
appId = window->GetDoc()->NodePrincipal()->GetAppId();
|
||||
}
|
||||
}
|
||||
|
||||
if (appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
@@ -1978,7 +1988,7 @@ Notification::ResolveIconAndSoundURL(nsString& iconUrl, nsString& soundUrl)
|
||||
if (mWorkerPrivate) {
|
||||
baseUri = mWorkerPrivate->GetBaseURI();
|
||||
} else {
|
||||
nsIDocument* doc = GetOwner()->GetExtantDoc();
|
||||
nsIDocument* doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr;
|
||||
if (doc) {
|
||||
baseUri = doc->GetBaseURI();
|
||||
charset = doc->GetDocumentCharacterSet().get();
|
||||
@@ -2755,7 +2765,7 @@ Notification::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
nsCOMPtr<nsIDocument> doc = window ? window->GetExtantDoc() : nullptr;
|
||||
nsCOMPtr<nsIPrincipal> nodePrincipal = doc ? doc->NodePrincipal() :
|
||||
nullptr;
|
||||
if (nodePrincipal) {
|
||||
|
||||
@@ -43,6 +43,30 @@ function testHaveXray() {
|
||||
nextTest();
|
||||
}
|
||||
|
||||
function testConstructor1() {
|
||||
var p = new win.Promise(function(resolve, reject) { resolve(win.Promise.resolve(5)); });
|
||||
p.then(
|
||||
function(arg) {
|
||||
is(arg, 5, "Content Promise constructor resolved with content promise should work");
|
||||
},
|
||||
function(e) {
|
||||
ok(false, "Content Promise constructor resolved with content promise should not fail");
|
||||
}
|
||||
).then(nextTest);
|
||||
}
|
||||
|
||||
function testConstructor2() {
|
||||
var p = new win.Promise(function(resolve, reject) { resolve(Promise.resolve(5)); });
|
||||
p.then(
|
||||
function(arg) {
|
||||
is(arg, 5, "Content Promise constructor resolved with chrome promise should work");
|
||||
},
|
||||
function(e) {
|
||||
ok(false, "Content Promise constructor resolved with chrome promise should not fail");
|
||||
}
|
||||
).then(nextTest);
|
||||
}
|
||||
|
||||
function testRace1() {
|
||||
var p = win.Promise.race(new win.Array(1, 2));
|
||||
p.then(
|
||||
@@ -250,7 +274,7 @@ function testThen2() {
|
||||
|
||||
function testCatch1() {
|
||||
var p = win.Promise.reject(5);
|
||||
ok(p instanceof win.Promise, "Promise.resolve should return a promise");
|
||||
ok(p instanceof win.Promise, "Promise.reject should return a promise");
|
||||
var q = p.catch((x) => x*x);
|
||||
ok(q instanceof win.Promise,
|
||||
"Promise.catch should return a promise from the right global");
|
||||
@@ -267,6 +291,8 @@ function testCatch1() {
|
||||
var tests = [
|
||||
testLoadComplete,
|
||||
testHaveXray,
|
||||
testConstructor1,
|
||||
testConstructor2,
|
||||
testRace1,
|
||||
testRace2,
|
||||
testRace3,
|
||||
|
||||
@@ -16,6 +16,12 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var isParent = Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||
|
||||
// Observer notification topics for system subscriptions. These are duplicated
|
||||
// and used in `PushNotifier.cpp`. They're exposed on `nsIPushService` instead
|
||||
// of `nsIPushNotifier` so that JS callers only need to import this service.
|
||||
const OBSERVER_TOPIC_PUSH = "push-message";
|
||||
const OBSERVER_TOPIC_SUBSCRIPTION_CHANGE = "push-subscription-change";
|
||||
|
||||
/**
|
||||
* `PushServiceBase`, `PushServiceParent`, and `PushServiceContent` collectively
|
||||
* implement the `nsIPushService` interface. This interface provides calls
|
||||
@@ -43,6 +49,9 @@ PushServiceBase.prototype = {
|
||||
Ci.nsIPushQuotaManager,
|
||||
]),
|
||||
|
||||
pushTopic: OBSERVER_TOPIC_PUSH,
|
||||
subscriptionChangeTopic: OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
|
||||
|
||||
_handleReady() {},
|
||||
|
||||
_addListeners() {
|
||||
|
||||
@@ -192,7 +192,7 @@ PushNotifier::NotifyPushObservers(const nsACString& aScope,
|
||||
if (aData) {
|
||||
message = new PushMessage(aData.ref());
|
||||
}
|
||||
return obsService->NotifyObservers(message, "push-message",
|
||||
return obsService->NotifyObservers(message, OBSERVER_TOPIC_PUSH,
|
||||
NS_ConvertUTF8toUTF16(aScope).get());
|
||||
}
|
||||
|
||||
@@ -204,7 +204,8 @@ PushNotifier::NotifySubscriptionChangeObservers(const nsACString& aScope)
|
||||
if (!obsService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return obsService->NotifyObservers(nullptr, "push-subscription-change",
|
||||
return obsService->NotifyObservers(nullptr,
|
||||
OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
|
||||
NS_ConvertUTF8toUTF16(aScope).get());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
#define PUSHNOTIFIER_CONTRACTID \
|
||||
"@mozilla.org/push/Notifier;1"
|
||||
|
||||
// These constants are duplicated in `PushComponents.js`.
|
||||
#define OBSERVER_TOPIC_PUSH "push-message"
|
||||
#define OBSERVER_TOPIC_SUBSCRIPTION_CHANGE "push-subscription-change"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
@@ -14,6 +14,11 @@ Cu.import('resource://gre/modules/Preferences.jsm');
|
||||
Cu.import('resource://gre/modules/PlacesUtils.jsm');
|
||||
Cu.import('resource://gre/modules/ObjectUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'PlacesTestUtils',
|
||||
'resource://testing-common/PlacesTestUtils.jsm');
|
||||
XPCOMUtils.defineLazyServiceGetter(this, 'PushServiceComponent',
|
||||
'@mozilla.org/push/Service;1', 'nsIPushService');
|
||||
|
||||
const serviceExports = Cu.import('resource://gre/modules/PushService.jsm', {});
|
||||
const servicePrefs = new Preferences('dom.push.');
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ add_task(function* setUp() {
|
||||
});
|
||||
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.com/expired-quota-restored'
|
||||
);
|
||||
|
||||
@@ -128,7 +128,7 @@ add_task(function* setUp() {
|
||||
|
||||
add_task(function* test_site_visited() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.xyz/expired-quota-exceeded'
|
||||
);
|
||||
|
||||
@@ -141,7 +141,7 @@ add_task(function* test_site_visited() {
|
||||
|
||||
add_task(function* test_perm_restored() {
|
||||
let subChangePromise = promiseObserverNotification(
|
||||
'push-subscription-change',
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
(subject, data) => data == 'https://example.info/expired-perm-revoked'
|
||||
);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ add_task(function* test_notification_ack() {
|
||||
}
|
||||
|
||||
let notifyCount = 0;
|
||||
let notifyPromise = promiseObserverNotification('push-message', () =>
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, () =>
|
||||
++notifyCount == 3);
|
||||
|
||||
let acks = 0;
|
||||
|
||||
@@ -218,7 +218,7 @@ add_task(function* test_notification_ack_data() {
|
||||
];
|
||||
|
||||
let sendAndReceive = testData => {
|
||||
let messageReceived = promiseObserverNotification('push-message', (subject, data) => {
|
||||
let messageReceived = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
let notification = subject.QueryInterface(Ci.nsIPushMessage);
|
||||
equal(notification.text(), testData.receive.data,
|
||||
'Check data for notification ' + testData.version);
|
||||
|
||||
@@ -40,7 +40,7 @@ add_task(function* test_notification_duplicate() {
|
||||
yield db.put(record);
|
||||
}
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-message');
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
|
||||
let acks = 0;
|
||||
let ackDone;
|
||||
|
||||
@@ -50,7 +50,7 @@ add_task(function* test_notification_error() {
|
||||
}
|
||||
|
||||
let scopes = [];
|
||||
let notifyPromise = promiseObserverNotification('push-message', (subject, data) =>
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) =>
|
||||
scopes.push(data) == 2);
|
||||
|
||||
let ackDone;
|
||||
|
||||
@@ -121,21 +121,21 @@ add_task(function* test_pushNotifications() {
|
||||
}
|
||||
|
||||
let notifyPromise = Promise.all([
|
||||
promiseObserverNotification('push-message', function(subject, data) {
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage);
|
||||
if (message && (data == "https://example.com/page/1")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification('push-message', function(subject, data) {
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage);
|
||||
if (message && (data == "https://example.com/page/2")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification('push-message', function(subject, data) {
|
||||
promiseObserverNotification(PushServiceComponent.pushTopic, function(subject, data) {
|
||||
var message = subject.QueryInterface(Ci.nsIPushMessage);
|
||||
if (message && (data == "https://example.com/page/3")){
|
||||
equal(message.text(), "Some message", "decoded message is incorrect");
|
||||
|
||||
@@ -55,8 +55,8 @@ add_task(function* test_notification_incomplete() {
|
||||
ok(false, 'Should not deliver malformed updates');
|
||||
}
|
||||
do_register_cleanup(() =>
|
||||
Services.obs.removeObserver(observeMessage, 'push-message'));
|
||||
Services.obs.addObserver(observeMessage, 'push-message', false);
|
||||
Services.obs.removeObserver(observeMessage, PushServiceComponent.pushTopic));
|
||||
Services.obs.addObserver(observeMessage, PushServiceComponent.pushTopic, false);
|
||||
|
||||
let notificationDone;
|
||||
let notificationPromise = new Promise(resolve => notificationDone = after(2, resolve));
|
||||
|
||||
@@ -28,7 +28,7 @@ add_task(function* test_notification_version_string() {
|
||||
systemRecord: true,
|
||||
});
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-message');
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = resolve);
|
||||
|
||||
@@ -52,7 +52,7 @@ function makePushPermission(url, capability) {
|
||||
|
||||
function promiseSubscriptionChanges(count) {
|
||||
let notifiedScopes = [];
|
||||
let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
|
||||
let subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == count;
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
// different scopes, so each can send 5 notifications before we remove
|
||||
// their subscription.
|
||||
let updates = 0;
|
||||
let notifyPromise = promiseObserverNotification('push-message', (subject, data) => {
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
updates++;
|
||||
return updates == 6;
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
|
||||
let unregisterDone;
|
||||
let unregisterPromise = new Promise(resolve => unregisterDone = resolve);
|
||||
let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) =>
|
||||
let subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) =>
|
||||
data == 'https://example.com/stuff');
|
||||
|
||||
PushService.init({
|
||||
@@ -109,7 +109,7 @@ add_task(function* test_expiration_history_observer() {
|
||||
strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
|
||||
|
||||
let notifiedScopes = [];
|
||||
subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
|
||||
subChangePromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic, (subject, data) => {
|
||||
notifiedScopes.push(data);
|
||||
return notifiedScopes.length == 2;
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ add_task(function* test_expiration_origin_threshold() {
|
||||
let numMessages = 10;
|
||||
|
||||
let updates = 0;
|
||||
let notifyPromise = promiseObserverNotification('push-message', (subject, data) => {
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic, (subject, data) => {
|
||||
updates++;
|
||||
return updates == numMessages;
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ add_task(function* test_register_flush() {
|
||||
};
|
||||
yield db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-message');
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.pushTopic);
|
||||
|
||||
let ackDone;
|
||||
let ackPromise = new Promise(resolve => ackDone = after(2, resolve));
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
|
||||
const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
|
||||
|
||||
var db, service;
|
||||
var db;
|
||||
|
||||
function run_test() {
|
||||
service = Cc['@mozilla.org/push/Service;1']
|
||||
.getService(Ci.nsIPushService);
|
||||
if (isParent) {
|
||||
do_get_profile();
|
||||
}
|
||||
@@ -26,7 +24,7 @@ if (isParent) {
|
||||
|
||||
add_test(function test_subscribe_success() {
|
||||
do_test_pending();
|
||||
service.subscribe(
|
||||
PushServiceComponent.subscribe(
|
||||
'https://example.com/sub/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
@@ -44,7 +42,7 @@ add_test(function test_subscribe_success() {
|
||||
|
||||
add_test(function test_subscribe_error() {
|
||||
do_test_pending();
|
||||
service.subscribe(
|
||||
PushServiceComponent.subscribe(
|
||||
'https://example.com/sub/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
@@ -59,7 +57,7 @@ add_test(function test_subscribe_error() {
|
||||
|
||||
add_test(function test_getSubscription_exists() {
|
||||
do_test_pending();
|
||||
service.getSubscription(
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
@@ -78,7 +76,7 @@ add_test(function test_getSubscription_exists() {
|
||||
|
||||
add_test(function test_getSubscription_missing() {
|
||||
do_test_pending();
|
||||
service.getSubscription(
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/missing',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
@@ -93,7 +91,7 @@ add_test(function test_getSubscription_missing() {
|
||||
|
||||
add_test(function test_getSubscription_error() {
|
||||
do_test_pending();
|
||||
service.getSubscription(
|
||||
PushServiceComponent.getSubscription(
|
||||
'https://example.com/get/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, subscription) => {
|
||||
@@ -108,7 +106,7 @@ add_test(function test_getSubscription_error() {
|
||||
|
||||
add_test(function test_unsubscribe_success() {
|
||||
do_test_pending();
|
||||
service.unsubscribe(
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
@@ -123,7 +121,7 @@ add_test(function test_unsubscribe_success() {
|
||||
|
||||
add_test(function test_unsubscribe_nonexistent() {
|
||||
do_test_pending();
|
||||
service.unsubscribe(
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/ok',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
@@ -138,7 +136,7 @@ add_test(function test_unsubscribe_nonexistent() {
|
||||
|
||||
add_test(function test_unsubscribe_error() {
|
||||
do_test_pending();
|
||||
service.unsubscribe(
|
||||
PushServiceComponent.unsubscribe(
|
||||
'https://example.com/unsub/fail',
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
(result, success) => {
|
||||
@@ -159,7 +157,7 @@ add_test(function test_subscribe_app_principal() {
|
||||
);
|
||||
|
||||
do_test_pending();
|
||||
service.subscribe('https://example.net/scope/1', principal, (result, subscription) => {
|
||||
PushServiceComponent.subscribe('https://example.net/scope/1', principal, (result, subscription) => {
|
||||
ok(Components.isSuccessCode(result), 'Error creating subscription');
|
||||
ok(subscription.endpoint.startsWith('https://example.org/push'),
|
||||
'Wrong push endpoint in app subscription');
|
||||
@@ -176,7 +174,7 @@ add_test(function test_subscribe_origin_principal() {
|
||||
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(scope);
|
||||
|
||||
do_test_pending();
|
||||
service.subscribe(scope, principal, (result, subscription) => {
|
||||
PushServiceComponent.subscribe(scope, principal, (result, subscription) => {
|
||||
ok(Components.isSuccessCode(result),
|
||||
'Expected error creating subscription with origin principal');
|
||||
equal(subscription.quota, 16, 'Wrong quota for origin subscription');
|
||||
@@ -188,7 +186,7 @@ add_test(function test_subscribe_origin_principal() {
|
||||
|
||||
add_test(function test_subscribe_null_principal() {
|
||||
do_test_pending();
|
||||
service.subscribe(
|
||||
PushServiceComponent.subscribe(
|
||||
'chrome://push/null-principal',
|
||||
Services.scriptSecurityManager.createNullPrincipal({}),
|
||||
(result, subscription) => {
|
||||
@@ -205,7 +203,7 @@ add_test(function test_subscribe_null_principal() {
|
||||
|
||||
add_test(function test_subscribe_missing_principal() {
|
||||
do_test_pending();
|
||||
service.subscribe('chrome://push/missing-principal', null,
|
||||
PushServiceComponent.subscribe('chrome://push/missing-principal', null,
|
||||
(result, subscription) => {
|
||||
ok(!Components.isSuccessCode(result),
|
||||
'Expected error creating subscription without principal');
|
||||
|
||||
@@ -15,8 +15,12 @@ add_task(function* test_service_parent() {
|
||||
do_register_cleanup(() => {return db.drop().then(_ => db.close());});
|
||||
yield setUpServiceInParent(PushService, db);
|
||||
|
||||
// Start the service in the main process.
|
||||
Cc['@mozilla.org/push/Service;1'].getService(Ci.nsIPushService);
|
||||
// Accessing the lazy service getter will start the service in the main
|
||||
// process.
|
||||
equal(PushServiceComponent.pushTopic, "push-message",
|
||||
"Wrong push message observer topic");
|
||||
equal(PushServiceComponent.subscriptionChangeTopic,
|
||||
"push-subscription-change", "Wrong subscription change observer topic");
|
||||
|
||||
yield run_test_in_child('./test_service_child.js');
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ add_task(function* test1() {
|
||||
|
||||
yield db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-subscription-change',
|
||||
let notifyPromise = promiseObserverNotification(PushServiceComponent.subscriptionChangeTopic,
|
||||
_ => true);
|
||||
|
||||
PushService.init({
|
||||
|
||||
+7
-7
@@ -224,12 +224,17 @@ TVTuner::InitMediaStream()
|
||||
already_AddRefed<DOMMediaStream>
|
||||
TVTuner::CreateSimulatedMediaStream()
|
||||
{
|
||||
ErrorResult error;
|
||||
nsCOMPtr<nsPIDOMWindow> domWin = GetOwner();
|
||||
if (NS_WARN_IF(!domWin)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* doc = GetOwner()->GetExtantDoc();
|
||||
nsIDocument* doc = domWin->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ErrorResult error;
|
||||
RefPtr<Element> element = doc->CreateElement(VIDEO_TAG, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return nullptr;
|
||||
@@ -255,11 +260,6 @@ TVTuner::CreateSimulatedMediaStream()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(GetOwner()));
|
||||
if (NS_WARN_IF(!domWin)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITVSimulatorService> simService(do_QueryInterface(mTVService));
|
||||
if (NS_WARN_IF(!simService)) {
|
||||
return nullptr;
|
||||
|
||||
+184
-108
@@ -42,6 +42,25 @@ function GetAttachedArrayBuffer(tarray) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// A function which ensures that the argument is either a typed array or a
|
||||
// cross-compartment wrapper for a typed array and that the typed array involved
|
||||
// has an attached array buffer. If one of those conditions doesn't hold (wrong
|
||||
// kind of argument, or detached array buffer), an exception is thrown. The
|
||||
// return value is `true` if the argument is a typed array, `false` if it's a
|
||||
// cross-compartment wrapper for a typed array.
|
||||
function IsTypedArrayEnsuringArrayBuffer(arg) {
|
||||
if (IsObject(arg) && IsTypedArray(arg)) {
|
||||
GetAttachedArrayBuffer(arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a bit hacky but gets the job done: the first `arg` is used to
|
||||
// test for a wrapped typed array, the second as an argument to
|
||||
// GetAttachedArrayBuffer.
|
||||
callFunction(CallTypedArrayMethodIfWrapped, arg, arg, "GetAttachedArrayBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ES6 draft 20150304 %TypedArray%.prototype.copyWithin
|
||||
function TypedArrayCopyWithin(target, start, end = undefined) {
|
||||
// This function is not generic.
|
||||
@@ -119,13 +138,20 @@ function TypedArrayEntries() {
|
||||
// Step 1.
|
||||
var O = this;
|
||||
|
||||
// Step 2-3.
|
||||
if (!IsObject(O) || !IsTypedArray(O)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayEntries");
|
||||
}
|
||||
// We need to be a bit careful here, because in the Xray case we want to
|
||||
// create the iterator in our current compartment.
|
||||
//
|
||||
// Before doing that, though, we want to check that we have a typed array
|
||||
// and it does not have a detached array buffer. We do the latter by just
|
||||
// calling GetAttachedArrayBuffer() and letting it throw if there isn't one.
|
||||
// In the case when we're not sure we have a typed array (e.g. we might have
|
||||
// a cross-compartment wrapper for one), we can go ahead and call
|
||||
// GetAttachedArrayBuffer via IsTypedArrayEnsuringArrayBuffer; that will
|
||||
// throw if we're not actually a wrapped typed array, or if we have a
|
||||
// detached array buffer.
|
||||
|
||||
// Step 4-6.
|
||||
GetAttachedArrayBuffer(O);
|
||||
// Step 2-6.
|
||||
IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// Step 7.
|
||||
return CreateArrayIterator(O, ITEM_KIND_KEY_AND_VALUE);
|
||||
@@ -133,19 +159,23 @@ function TypedArrayEntries() {
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.3.7 %TypedArray%.prototype.every(callbackfn[, thisArg]).
|
||||
function TypedArrayEvery(callbackfn, thisArg = undefined) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
|
||||
"TypedArrayEvery");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -222,15 +252,19 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) {
|
||||
|
||||
// Steps 2-3.
|
||||
// This function is not generic.
|
||||
if (!IsObject(O) || !IsTypedArray(O)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
|
||||
"TypedArrayFilter");
|
||||
}
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
GetAttachedArrayBuffer(O);
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Step 4.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
if (arguments.length === 0)
|
||||
@@ -283,19 +317,23 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) {
|
||||
|
||||
// ES6 draft rev28 (2014/10/14) 22.2.3.10 %TypedArray%.prototype.find(predicate[, thisArg]).
|
||||
function TypedArrayFind(predicate, thisArg = undefined) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
|
||||
"TypedArrayFind");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -322,19 +360,23 @@ function TypedArrayFind(predicate, thisArg = undefined) {
|
||||
|
||||
// ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate[, thisArg]).
|
||||
function TypedArrayFindIndex(predicate, thisArg = undefined) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
|
||||
"TypedArrayFindIndex");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -359,19 +401,23 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) {
|
||||
|
||||
// ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
|
||||
function TypedArrayForEach(callbackfn, thisArg = undefined) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
|
||||
"TypedArrayForEach");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Step 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Step 3-4.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
if (arguments.length === 0)
|
||||
@@ -500,12 +546,10 @@ function TypedArrayKeys() {
|
||||
// Step 1.
|
||||
var O = this;
|
||||
|
||||
// Step 2.
|
||||
if (!IsObject(O) || !IsTypedArray(O)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayKeys");
|
||||
}
|
||||
// See the big comment in TypedArrayEntries for what we're doing here.
|
||||
|
||||
GetAttachedArrayBuffer(O);
|
||||
// Step 2.
|
||||
IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// Step 3.
|
||||
return CreateArrayIterator(O, ITEM_KIND_KEY);
|
||||
@@ -555,15 +599,19 @@ function TypedArrayMap(callbackfn, thisArg = undefined) {
|
||||
|
||||
// Steps 2-3.
|
||||
// This function is not generic.
|
||||
if (!IsObject(O) || !IsTypedArray(O)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
|
||||
"TypedArrayMap");
|
||||
}
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
GetAttachedArrayBuffer(O);
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Step 4.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
if (arguments.length === 0)
|
||||
@@ -597,17 +645,23 @@ function TypedArrayMap(callbackfn, thisArg = undefined) {
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.3.19 %TypedArray%.prototype.reduce(callbackfn[, initialValue]).
|
||||
function TypedArrayReduce(callbackfn/*, initialValue*/) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this))
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduce");
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -638,17 +692,23 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) {
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.3.20 %TypedArray%.prototype.reduceRight(callbackfn[, initialValue]).
|
||||
function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this))
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduceRight");
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -899,19 +959,23 @@ function TypedArraySlice(start, end) {
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
|
||||
function TypedArraySome(callbackfn, thisArg = undefined) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
|
||||
"TypedArraySome");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(this);
|
||||
|
||||
// Steps 1-2.
|
||||
var O = this;
|
||||
|
||||
// This function is not generic.
|
||||
// We want to make sure that we have an attached buffer, per spec prose.
|
||||
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// If we got here, `this` is either a typed array or a cross-compartment
|
||||
// wrapper for one.
|
||||
|
||||
// Steps 3-5.
|
||||
var len = TypedArrayLength(O);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(O);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength");
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if (arguments.length === 0)
|
||||
@@ -972,19 +1036,26 @@ function TypedArrayCompare(x, y) {
|
||||
// ES6 draft 20151210 22.2.3.26 %TypedArray%.prototype.sort ( comparefn ).
|
||||
function TypedArraySort(comparefn) {
|
||||
// This function is not generic.
|
||||
if (!IsObject(this) || !IsTypedArray(this)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, this, comparefn,
|
||||
"TypedArraySort");
|
||||
}
|
||||
|
||||
// Step 1.
|
||||
var obj = this;
|
||||
|
||||
// Step 2.
|
||||
var buffer = GetAttachedArrayBuffer(obj);
|
||||
var isTypedArray = IsObject(obj) && IsTypedArray(obj);
|
||||
|
||||
var buffer;
|
||||
if (isTypedArray)
|
||||
buffer = GetAttachedArrayBuffer(obj);
|
||||
else
|
||||
buffer = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "GetAttachedArrayBuffer");
|
||||
|
||||
// Step 3.
|
||||
var len = TypedArrayLength(obj);
|
||||
var len;
|
||||
if (isTypedArray) {
|
||||
len = TypedArrayLength(obj);
|
||||
} else {
|
||||
len = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength");
|
||||
}
|
||||
|
||||
if (comparefn === undefined) {
|
||||
comparefn = TypedArrayCompare;
|
||||
@@ -1004,20 +1075,30 @@ function TypedArraySort(comparefn) {
|
||||
} else if (IsFloat32TypedArray(obj)) {
|
||||
return RadixSort(obj, len, 4 /* nbytes */, true /* signed */, true /* floating */, comparefn);
|
||||
}
|
||||
// To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
|
||||
// the user supplied comparefn is wrapped.
|
||||
var wrappedCompareFn = comparefn;
|
||||
comparefn = function(x, y) {
|
||||
// Step a.
|
||||
var v = wrappedCompareFn(x, y);
|
||||
// Step b.
|
||||
if (IsDetachedBuffer(buffer))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
// Step c. is redundant, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
|
||||
// Step d.
|
||||
return v;
|
||||
}
|
||||
|
||||
// To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
|
||||
// the user supplied comparefn is wrapped.
|
||||
var wrappedCompareFn = comparefn;
|
||||
comparefn = function(x, y) {
|
||||
// Step a.
|
||||
var v = wrappedCompareFn(x, y);
|
||||
// Step b.
|
||||
var bufferDetached;
|
||||
if (isTypedArray) {
|
||||
bufferDetached = IsDetachedBuffer(buffer);
|
||||
} else {
|
||||
// This is totally cheating and only works because we know `this`
|
||||
// and `buffer` are same-compartment".
|
||||
bufferDetached = callFunction(CallTypedArrayMethodIfWrapped, this,
|
||||
buffer, "IsDetachedBuffer");
|
||||
}
|
||||
if (bufferDetached)
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
// Step c. is redundant, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1121937#c36
|
||||
// Step d.
|
||||
return v;
|
||||
}
|
||||
|
||||
return QuickSort(obj, len, comparefn);
|
||||
@@ -1074,13 +1155,8 @@ function TypedArrayValues() {
|
||||
// Step 1.
|
||||
var O = this;
|
||||
|
||||
// Step 2-3.
|
||||
if (!IsObject(O) || !IsTypedArray(O)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayValues");
|
||||
}
|
||||
|
||||
// Step 4-6.
|
||||
GetAttachedArrayBuffer(O);
|
||||
// See the big comment in TypedArrayEntries for what we're doing here.
|
||||
IsTypedArrayEnsuringArrayBuffer(O);
|
||||
|
||||
// Step 7.
|
||||
return CreateArrayIterator(O, ITEM_KIND_VALUE);
|
||||
|
||||
@@ -4980,6 +4980,7 @@ BytecodeEmitter::emitCatch(ParseNode* pn)
|
||||
StmtInfoBCE* stmt = innermostStmt();
|
||||
MOZ_ASSERT(stmt->type == StmtType::BLOCK && stmt->isBlockScope);
|
||||
stmt->type = StmtType::CATCH;
|
||||
stmt->staticBlock().setIsForCatchParameters();
|
||||
|
||||
/* Go up one statement info record to the TRY or FINALLY record. */
|
||||
stmt = stmt->enclosing;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// |jit-test| error: TypeError
|
||||
//
|
||||
// Make sure we can recover missing arguments even when it gets assigned to
|
||||
// another slot.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "evalInFrame.js");
|
||||
|
||||
function h() {
|
||||
@@ -14,4 +13,13 @@ function f() {
|
||||
h();
|
||||
}
|
||||
|
||||
f();
|
||||
assertThrowsInstanceOf(f, TypeError);
|
||||
|
||||
function g() {
|
||||
{
|
||||
let a = arguments;
|
||||
h();
|
||||
}
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(g, TypeError);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
g = newGlobal();
|
||||
g.parent = this;
|
||||
|
||||
function installHook() {
|
||||
let calledTimes = 0;
|
||||
function hook() {
|
||||
calledTimes++;
|
||||
|
||||
// Allow the new.target.prototype get to throw.
|
||||
if (calledTimes === 1)
|
||||
return undefined;
|
||||
|
||||
return {
|
||||
return: undefined
|
||||
};
|
||||
}
|
||||
|
||||
Debugger(parent).onExceptionUnwind = hook;
|
||||
}
|
||||
|
||||
|
||||
g.eval("(" + installHook + ")()");
|
||||
|
||||
var handler = {
|
||||
get(t, p) {
|
||||
throw new TypeError;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var f = new Proxy(function(){}, handler);
|
||||
new f();
|
||||
@@ -0,0 +1,49 @@
|
||||
g = newGlobal();
|
||||
g.parent = this;
|
||||
|
||||
function installHook() {
|
||||
let calledTimes = 0;
|
||||
function hook(frame) {
|
||||
calledTimes++;
|
||||
switch (calledTimes) {
|
||||
case 1:
|
||||
// Proxy get trap
|
||||
assertEq(frame.type, "call");
|
||||
assertEq(frame.script.displayName.includes("get"), true);
|
||||
break;
|
||||
case 2:
|
||||
// wrapper function. There is no entry for notRun
|
||||
assertEq(frame.type, "call");
|
||||
assertEq(frame.script.displayName.includes("wrapper"), true);
|
||||
break;
|
||||
case 3:
|
||||
assertEq(frame.type, "global");
|
||||
// Force the top-level to return cleanly, so that we can tell
|
||||
// assertion failures from the intended throwing.
|
||||
return { return: undefined };
|
||||
|
||||
default:
|
||||
// that's the whole chain.
|
||||
assertEq(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
Debugger(parent).onExceptionUnwind = hook;
|
||||
}
|
||||
|
||||
|
||||
g.eval("(" + installHook + ")()");
|
||||
|
||||
var handler = {
|
||||
get(t, p) {
|
||||
throw new TypeError;
|
||||
}
|
||||
};
|
||||
|
||||
function notRun() {}
|
||||
|
||||
function wrapper() {
|
||||
var f = new Proxy(notRun, handler);
|
||||
new f();
|
||||
}
|
||||
wrapper();
|
||||
+1
-1
@@ -453,7 +453,7 @@ class JSObject : public js::gc::Cell
|
||||
* this will just be the global (the name "enclosing scope" still applies
|
||||
* in this situation because non-scope objects can be on the scope chain).
|
||||
*/
|
||||
inline JSObject* enclosingScope();
|
||||
inline JSObject* enclosingScope() const;
|
||||
|
||||
inline js::GlobalObject& global() const;
|
||||
inline bool isOwnGlobal() const;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Tests annex B.3.5 that introduces a var via direct eval.
|
||||
|
||||
var x = "global-x";
|
||||
var log = "";
|
||||
|
||||
// Tests that direct eval works.
|
||||
function g() {
|
||||
try { throw 8; } catch (x) {
|
||||
eval("var x = 42;");
|
||||
log += x;
|
||||
}
|
||||
x = "g";
|
||||
log += x;
|
||||
}
|
||||
g();
|
||||
|
||||
assertEq(x, "global-x");
|
||||
assertEq(log, "42g");
|
||||
|
||||
if ("reportCompare" in this)
|
||||
reportCompare(true, true)
|
||||
@@ -37,8 +37,10 @@ for (var constructor of constructors) {
|
||||
|
||||
// Called from other globals.
|
||||
if (typeof newGlobal === "function" && !isSharedConstructor(constructor)) {
|
||||
var entries = newGlobal()[constructor.name].prototype.entries;
|
||||
assertDeepEq([...entries.call(new constructor(2))], [[0, 0], [1, 0]]);
|
||||
var otherGlobal = newGlobal();
|
||||
var entries = otherGlobal[constructor.name].prototype.entries;
|
||||
assertDeepEq([...entries.call(new constructor(2))],
|
||||
[new otherGlobal.Array(0, 0), new otherGlobal.Array(1, 0)]);
|
||||
arr = new (newGlobal()[constructor.name])(2);
|
||||
assertEq([...constructor.prototype.entries.call(arr)].toString(), "0,0,1,0");
|
||||
}
|
||||
|
||||
+22
-10
@@ -182,10 +182,8 @@ js::Debug_CheckSelfHosted(JSContext* cx, HandleValue fun)
|
||||
MOZ_CRASH("self-hosted checks should only be done in Debug builds");
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(fun.isObject());
|
||||
|
||||
MOZ_ASSERT(fun.toObject().is<JSFunction>());
|
||||
MOZ_ASSERT(fun.toObject().as<JSFunction>().isSelfHostedOrIntrinsic());
|
||||
RootedObject funObj(cx, UncheckedUnwrap(&fun.toObject()));
|
||||
MOZ_ASSERT(funObj->as<JSFunction>().isSelfHostedOrIntrinsic());
|
||||
|
||||
// This is purely to police self-hosted code. There is no actual operation.
|
||||
return true;
|
||||
@@ -1704,9 +1702,10 @@ Interpret(JSContext* cx, RunState& state)
|
||||
|
||||
/* State communicated between non-local jumps: */
|
||||
bool interpReturnOK;
|
||||
bool frameHalfInitialized;
|
||||
|
||||
if (!activation.entryFrame()->prologue(cx))
|
||||
goto error;
|
||||
goto prologue_error;
|
||||
|
||||
switch (Debugger::onEnterFrame(cx, activation.entryFrame())) {
|
||||
case JSTRAP_CONTINUE:
|
||||
@@ -1940,15 +1939,21 @@ CASE(JSOP_RETRVAL)
|
||||
interpReturnOK = true;
|
||||
|
||||
return_continuation:
|
||||
frameHalfInitialized = false;
|
||||
|
||||
prologue_return_continuation:
|
||||
|
||||
if (activation.entryFrame() != REGS.fp()) {
|
||||
// Stop the engine. (No details about which engine exactly, could be
|
||||
// interpreter, Baseline or IonMonkey.)
|
||||
TraceLogStopEvent(logger, TraceLogger_Engine);
|
||||
TraceLogStopEvent(logger, TraceLogger_Scripts);
|
||||
|
||||
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
if (MOZ_LIKELY(!frameHalfInitialized)) {
|
||||
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
|
||||
REGS.fp()->epilogue(cx);
|
||||
REGS.fp()->epilogue(cx);
|
||||
}
|
||||
|
||||
jit_return_pop_frame:
|
||||
|
||||
@@ -2909,7 +2914,7 @@ CASE(JSOP_FUNCALL)
|
||||
}
|
||||
|
||||
if (!REGS.fp()->prologue(cx))
|
||||
goto error;
|
||||
goto prologue_error;
|
||||
|
||||
switch (Debugger::onEnterFrame(cx, REGS.fp())) {
|
||||
case JSTRAP_CONTINUE:
|
||||
@@ -4032,9 +4037,11 @@ DEFAULT()
|
||||
MOZ_CRASH("Invalid HandleError continuation");
|
||||
|
||||
exit:
|
||||
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
if (MOZ_LIKELY(!frameHalfInitialized)) {
|
||||
interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
|
||||
|
||||
REGS.fp()->epilogue(cx);
|
||||
REGS.fp()->epilogue(cx);
|
||||
}
|
||||
|
||||
gc::MaybeVerifyBarriers(cx, true);
|
||||
|
||||
@@ -4051,6 +4058,11 @@ DEFAULT()
|
||||
state.setReturnValue(activation.entryFrame()->returnValue());
|
||||
|
||||
return interpReturnOK;
|
||||
|
||||
prologue_error:
|
||||
interpReturnOK = false;
|
||||
frameHalfInitialized = true;
|
||||
goto prologue_return_continuation;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -240,7 +240,7 @@ StaticScopeIter<allowGC>::module() const
|
||||
} /* namespace js */
|
||||
|
||||
inline JSObject*
|
||||
JSObject::enclosingScope()
|
||||
JSObject::enclosingScope() const
|
||||
{
|
||||
if (is<js::ScopeObject>())
|
||||
return &as<js::ScopeObject>().enclosingScope();
|
||||
|
||||
@@ -1022,7 +1022,7 @@ ClonedBlockObject::createGlobal(JSContext* cx, Handle<GlobalObject*> global)
|
||||
|
||||
// Currently the global lexical scope cannot have any bindings with frame
|
||||
// slots.
|
||||
staticLexical->setLocalOffset(UINT32_MAX);
|
||||
staticLexical->setLocalOffsetToInvalid();
|
||||
staticLexical->initEnclosingScope(nullptr);
|
||||
Rooted<ClonedBlockObject*> lexical(cx, ClonedBlockObject::create(cx, staticLexical, global));
|
||||
if (!lexical)
|
||||
@@ -1043,7 +1043,7 @@ ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStati
|
||||
if (!staticLexical)
|
||||
return nullptr;
|
||||
|
||||
staticLexical->setLocalOffset(UINT32_MAX);
|
||||
staticLexical->setLocalOffsetToInvalid();
|
||||
staticLexical->initEnclosingScope(enclosingStatic);
|
||||
Rooted<ClonedBlockObject*> lexical(cx, ClonedBlockObject::create(cx, staticLexical,
|
||||
enclosingScope));
|
||||
@@ -1948,9 +1948,38 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
static bool isMagicMissingArgumentsValue(JSContext* cx, ScopeObject& scope, HandleValue v)
|
||||
{
|
||||
bool isMagic = v.isMagic() && v.whyMagic() == JS_OPTIMIZED_ARGUMENTS;
|
||||
MOZ_ASSERT_IF(isMagic,
|
||||
isFunctionScope(scope) &&
|
||||
scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding());
|
||||
|
||||
#ifdef DEBUG
|
||||
// The |scope| object here is not limited to CallObjects but may also
|
||||
// be block scopes in case of the following:
|
||||
//
|
||||
// function f() { { let a = arguments; } }
|
||||
//
|
||||
// We need to check that |scope|'s static scope's nearest function
|
||||
// scope has an 'arguments' var binding. The dynamic scope chain is
|
||||
// not sufficient: |f| above will not have a CallObject because there
|
||||
// are no aliased body-level bindings.
|
||||
if (isMagic) {
|
||||
JSFunction* callee = nullptr;
|
||||
if (isFunctionScope(scope)) {
|
||||
callee = &scope.as<CallObject>().callee();
|
||||
} else {
|
||||
// We will never have a DynamicWithObject here because no
|
||||
// binding accesses on with scopes are unaliased.
|
||||
for (StaticScopeIter<NoGC> ssi(&scope.as<ClonedBlockObject>().staticBlock());
|
||||
!ssi.done();
|
||||
ssi++)
|
||||
{
|
||||
if (ssi.type() == StaticScopeIter<NoGC>::Function) {
|
||||
callee = &ssi.fun();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(callee && callee->nonLazyScript()->argumentsHasVarBinding());
|
||||
}
|
||||
#endif
|
||||
|
||||
return isMagic;
|
||||
}
|
||||
|
||||
@@ -3393,8 +3422,12 @@ js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
// Check that a direct eval will not hoist 'var' bindings over lexical
|
||||
// bindings with the same name.
|
||||
while (obj != varObj) {
|
||||
if (!CheckVarNameConflictsInScope<ClonedBlockObject>(cx, script, obj))
|
||||
return false;
|
||||
// Annex B.3.5 says 'var' declarations with the same name as catch
|
||||
// parameters are allowed.
|
||||
if (!obj->is<ClonedBlockObject>() || !obj->as<ClonedBlockObject>().isForCatchParameters()) {
|
||||
if (!CheckVarNameConflictsInScope<ClonedBlockObject>(cx, script, obj))
|
||||
return false;
|
||||
}
|
||||
obj = obj->enclosingScope();
|
||||
}
|
||||
|
||||
|
||||
+45
-7
@@ -130,10 +130,14 @@ class NestedStaticScope : public StaticScope
|
||||
*/
|
||||
class StaticBlockScope : public NestedStaticScope
|
||||
{
|
||||
static const unsigned LOCAL_OFFSET_SLOT = NestedStaticScope::RESERVED_SLOTS;
|
||||
static const unsigned LOCAL_OFFSET_AND_FLAGS_SLOT = NestedStaticScope::RESERVED_SLOTS;
|
||||
|
||||
static const uint32_t LocalOffsetShift = 1;
|
||||
static const uint32_t BlockFlagsMask = 0x1;
|
||||
static const uint32_t IsForCatchParametersFlag = 0x1;
|
||||
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = LOCAL_OFFSET_SLOT + 1;
|
||||
static const unsigned RESERVED_SLOTS = LOCAL_OFFSET_AND_FLAGS_SLOT + 1;
|
||||
|
||||
/* Return the number of variables associated with this block. */
|
||||
uint32_t numVariables() const {
|
||||
@@ -154,6 +158,20 @@ class StaticBlockScope : public NestedStaticScope
|
||||
setSlot(RESERVED_SLOTS + i, v);
|
||||
}
|
||||
|
||||
uint32_t localOffsetAndBlockFlags() const {
|
||||
return getReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT).toPrivateUint32();
|
||||
}
|
||||
|
||||
uint32_t blockFlags() const {
|
||||
return localOffsetAndBlockFlags() & BlockFlagsMask;
|
||||
}
|
||||
|
||||
void setBlockFlags(uint32_t flags) {
|
||||
MOZ_ASSERT((flags & ~BlockFlagsMask) == 0);
|
||||
setReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT,
|
||||
PrivateUint32Value(localOffsetAndBlockFlags() | flags));
|
||||
}
|
||||
|
||||
public:
|
||||
static StaticBlockScope* create(ExclusiveContext* cx);
|
||||
|
||||
@@ -184,15 +202,15 @@ class StaticBlockScope : public NestedStaticScope
|
||||
*/
|
||||
inline StaticBlockScope* enclosingBlock() const;
|
||||
|
||||
uint32_t localOffset() {
|
||||
return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
|
||||
uint32_t localOffset() const {
|
||||
return localOffsetAndBlockFlags() >> LocalOffsetShift;
|
||||
}
|
||||
|
||||
// Return the local corresponding to the 'var'th binding where 'var' is in the
|
||||
// range [0, numVariables()).
|
||||
uint32_t blockIndexToLocalIndex(uint32_t index) {
|
||||
MOZ_ASSERT(index < numVariables());
|
||||
return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32() + index;
|
||||
return localOffset() + index;
|
||||
}
|
||||
|
||||
// Return the slot corresponding to block index 'index', where 'index' is
|
||||
@@ -239,6 +257,10 @@ class StaticBlockScope : public NestedStaticScope
|
||||
return !isExtensible() || isGlobal();
|
||||
}
|
||||
|
||||
bool isForCatchParameters() const {
|
||||
return blockFlags() & IsForCatchParametersFlag;
|
||||
}
|
||||
|
||||
/* Frontend-only functions ***********************************************/
|
||||
|
||||
/* Initialization functions for above fields. */
|
||||
@@ -252,8 +274,20 @@ class StaticBlockScope : public NestedStaticScope
|
||||
}
|
||||
|
||||
void setLocalOffset(uint32_t offset) {
|
||||
MOZ_ASSERT(getReservedSlot(LOCAL_OFFSET_SLOT).isUndefined());
|
||||
initReservedSlot(LOCAL_OFFSET_SLOT, PrivateUint32Value(offset));
|
||||
MOZ_ASSERT(getReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT).isUndefined());
|
||||
MOZ_ASSERT(offset < LOCALNO_LIMIT);
|
||||
initReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT,
|
||||
PrivateUint32Value(offset << LocalOffsetShift));
|
||||
}
|
||||
|
||||
void setLocalOffsetToInvalid() {
|
||||
MOZ_ASSERT(getReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT).isUndefined());
|
||||
initReservedSlot(LOCAL_OFFSET_AND_FLAGS_SLOT,
|
||||
PrivateUint32Value(LOCALNO_LIMIT << LocalOffsetShift));
|
||||
}
|
||||
|
||||
void setIsForCatchParameters() {
|
||||
setBlockFlags(IsForCatchParametersFlag);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -997,6 +1031,10 @@ class ClonedBlockObject : public NestedScopeObject
|
||||
return !isExtensible() || isGlobal();
|
||||
}
|
||||
|
||||
bool isForCatchParameters() const {
|
||||
return staticBlock().isForCatchParameters();
|
||||
}
|
||||
|
||||
/* Copy in all the unaliased formals and locals. */
|
||||
void copyUnaliasedValues(AbstractFramePtr frame);
|
||||
|
||||
|
||||
+107
-6
@@ -138,6 +138,59 @@ intrinsic_IsConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intrinsic for calling a wrapped self-hosted function without invoking the
|
||||
* wrapper's security checks.
|
||||
*
|
||||
* Takes a wrapped function as the first and the receiver object as the
|
||||
* second argument. Any additional arguments are passed on to the unwrapped
|
||||
* function.
|
||||
*
|
||||
* Xray wrappers prevent lower-privileged code from passing objects to wrapped
|
||||
* functions from higher-privileged realms. In some cases, this check is too
|
||||
* strict, so this intrinsic allows getting around it.
|
||||
*
|
||||
* Note that it's not possible to replace all usages with dedicated intrinsics
|
||||
* as the function in question might be an inner function that closes over
|
||||
* state relevant to its execution.
|
||||
*
|
||||
* Right now, this is used for the Promise implementation to enable creating
|
||||
* resolution functions for xrayed Promises in the privileged realm and then
|
||||
* creating the Promise instance in the non-privileged one. The callbacks have
|
||||
* to be called by non-privileged code in various places, in many cases
|
||||
* passing objects as arguments.
|
||||
*/
|
||||
static bool
|
||||
intrinsic_UnsafeCallWrappedFunction(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() >= 2);
|
||||
MOZ_ASSERT(IsCallable(args[0]));
|
||||
MOZ_ASSERT(IsWrapper(&args[0].toObject()));
|
||||
MOZ_ASSERT(args[1].isObject() || args[1].isUndefined());
|
||||
|
||||
MOZ_RELEASE_ASSERT(args[0].isObject());
|
||||
RootedObject wrappedFun(cx, &args[0].toObject());
|
||||
RootedObject fun(cx, UncheckedUnwrap(wrappedFun));
|
||||
MOZ_RELEASE_ASSERT(fun->is<JSFunction>());
|
||||
MOZ_RELEASE_ASSERT(fun->as<JSFunction>().isSelfHostedBuiltin());
|
||||
|
||||
InvokeArgs args2(cx);
|
||||
if (!args2.init(args.length() - 2))
|
||||
return false;
|
||||
|
||||
args2.setThis(args[1]);
|
||||
|
||||
for (size_t i = 0; i < args2.length(); i++)
|
||||
args2[i].set(args[i + 2]);
|
||||
|
||||
AutoWaivePolicy waivePolicy(cx, wrappedFun, JSID_VOIDHANDLE, BaseProxyHandler::CALL);
|
||||
if (!CrossCompartmentWrapper::singleton.call(cx, wrappedFun, args2))
|
||||
return false;
|
||||
args.rval().set(args2.rval());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool
|
||||
intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
|
||||
@@ -561,6 +614,34 @@ intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value*
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intrinsic for creating an empty array in the compartment of the object
|
||||
* passed as the first argument.
|
||||
*
|
||||
* Returns the array, wrapped in the default wrapper to use between the two
|
||||
* compartments.
|
||||
*/
|
||||
static bool
|
||||
intrinsic_NewArrayInCompartment(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
RootedObject wrapped(cx, &args[0].toObject());
|
||||
MOZ_ASSERT(IsWrapper(wrapped));
|
||||
RootedObject obj(cx, UncheckedUnwrap(wrapped));
|
||||
|
||||
RootedArrayObject arr(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, obj);
|
||||
arr = NewDenseEmptyArray(cx);
|
||||
if (!arr)
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*arr);
|
||||
return wrapped->compartment()->wrap(cx, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -1441,16 +1522,31 @@ js::ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args)
|
||||
// because they always call the different CallXXXMethodIfWrapped methods,
|
||||
// which would be reported as the called function instead.
|
||||
|
||||
// Lookup the selfhosted method that was invoked.
|
||||
// Lookup the selfhosted method that was invoked. But skip over
|
||||
// IsTypedArrayEnsuringArrayBuffer frames, because those are never the
|
||||
// actual self-hosted callee from external code. We can't just skip
|
||||
// self-hosted things until we find a non-self-hosted one because of cases
|
||||
// like array.sort(somethingSelfHosted), where we want to report the error
|
||||
// in the somethingSelfHosted, not in the sort() call.
|
||||
ScriptFrameIter iter(cx);
|
||||
MOZ_ASSERT(iter.isFunctionFrame());
|
||||
|
||||
JSAutoByteString funNameBytes;
|
||||
if (const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes)) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
|
||||
funName, "method", InformalValueTypeName(args.thisv()));
|
||||
while (!iter.done()) {
|
||||
MOZ_ASSERT(iter.callee(cx)->isSelfHostedOrIntrinsic() &&
|
||||
!iter.callee(cx)->isBoundFunction());
|
||||
JSAutoByteString funNameBytes;
|
||||
const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes);
|
||||
if (!funName)
|
||||
return false;
|
||||
if (strcmp(funName, "IsTypedArrayEnsuringArrayBuffer") != 0) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
|
||||
funName, "method", InformalValueTypeName(args.thisv()));
|
||||
return false;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("How did we not find a useful self-hosted frame?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1539,9 +1635,11 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[0].toObject().is<TypedArrayObject>());
|
||||
|
||||
RootedObject object(cx, &args[0].toObject());
|
||||
object = CheckedUnwrap(object);
|
||||
MOZ_ASSERT(object->is<TypedArrayObject>());
|
||||
|
||||
JSProtoKey protoKey = StandardProtoKeyOrNull(object);
|
||||
MOZ_ASSERT(protoKey);
|
||||
|
||||
@@ -1848,6 +1946,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_INLINABLE_FN("UnsafeGetBooleanFromReservedSlot", intrinsic_UnsafeGetBooleanFromReservedSlot,2,0,
|
||||
IntrinsicUnsafeGetBooleanFromReservedSlot),
|
||||
|
||||
JS_FN("UnsafeCallWrappedFunction", intrinsic_UnsafeCallWrappedFunction,2,0),
|
||||
JS_FN("NewArrayInCompartment", intrinsic_NewArrayInCompartment, 1,0),
|
||||
|
||||
JS_FN("IsPackedArray", intrinsic_IsPackedArray, 1,0),
|
||||
|
||||
JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
|
||||
|
||||
@@ -405,6 +405,75 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
"getOwnPropertySymbols works on Xrayed ctors");
|
||||
}
|
||||
|
||||
// We will need arraysEqual and testArrayIterators both in this global scope
|
||||
// and in sandboxes, so define them as strings up front.
|
||||
var arraysEqualSource = `function arraysEqual(arr1, arr2, reason) {
|
||||
is(arr1.length, arr2.length, \`\${reason}; lengths should be equal\`)
|
||||
for (var i = 0; i < arr1.length; ++i) {
|
||||
if (Array.isArray(arr2[i])) {
|
||||
arraysEqual(arr1[i], arr2[i], \`\${reason}; item at index \${i}\`);
|
||||
} else {
|
||||
is(arr1[i], arr2[i], \`\${reason}; item at index \${i} should be equal\`);
|
||||
}
|
||||
}
|
||||
}`;
|
||||
eval(arraysEqualSource);
|
||||
|
||||
var testArrayIteratorsSource = `
|
||||
function testArrayIterators(arrayLike, equivalentArray, reason) {
|
||||
arraysEqual([...arrayLike], equivalentArray, \`\${reason}; spread operator\`);
|
||||
arraysEqual([...arrayLike.entries()], [...equivalentArray.entries()],
|
||||
\`\${reason}; entries\`);
|
||||
arraysEqual([...arrayLike.keys()], [...equivalentArray.keys()],
|
||||
\`\${reason}; keys\`);
|
||||
if (arrayLike.values) {
|
||||
arraysEqual([...arrayLike.values()], equivalentArray,
|
||||
\`\${reason}; values\`);
|
||||
}
|
||||
|
||||
var forEachCopy = [];
|
||||
arrayLike.forEach(function(arg) { forEachCopy.push(arg); });
|
||||
arraysEqual(forEachCopy, equivalentArray, \`\${reason}; forEach copy\`);
|
||||
|
||||
var everyCopy = [];
|
||||
arrayLike.every(function(arg) { everyCopy.push(arg); return true; });
|
||||
arraysEqual(everyCopy, equivalentArray, \`\${reason}; every() copy\`);
|
||||
|
||||
var filterCopy = [];
|
||||
var filterResult = arrayLike.filter(function(arg) {
|
||||
filterCopy.push(arg);
|
||||
return true;
|
||||
});
|
||||
arraysEqual(filterCopy, equivalentArray, \`\${reason}; filter copy\`);
|
||||
arraysEqual([...filterResult], equivalentArray, \`\${reason}; filter result\`);
|
||||
|
||||
var findCopy = [];
|
||||
arrayLike.find(function(arg) { findCopy.push(arg); return false; });
|
||||
arraysEqual(findCopy, equivalentArray, \`\${reason}; find() copy\`);
|
||||
|
||||
var findIndexCopy = [];
|
||||
arrayLike.findIndex(function(arg) { findIndexCopy.push(arg); return false; });
|
||||
arraysEqual(findIndexCopy, equivalentArray, \`\${reason}; findIndex() copy\`);
|
||||
|
||||
var mapCopy = [];
|
||||
var mapResult = arrayLike.map(function(arg) { mapCopy.push(arg); return arg});
|
||||
arraysEqual(mapCopy, equivalentArray, \`\${reason}; map() copy\`);
|
||||
arraysEqual([...mapResult], equivalentArray, \`\${reason}; map() result\`);
|
||||
|
||||
var reduceCopy = [];
|
||||
arrayLike.reduce(function(_, arg) { reduceCopy.push(arg); }, 0);
|
||||
arraysEqual(reduceCopy, equivalentArray, \`\${reason}; reduce() copy\`);
|
||||
|
||||
var reduceRightCopy = [];
|
||||
arrayLike.reduceRight(function(_, arg) { reduceRightCopy.unshift(arg); }, 0);
|
||||
arraysEqual(reduceRightCopy, equivalentArray, \`\${reason}; reduceRight() copy\`);
|
||||
|
||||
var someCopy = [];
|
||||
arrayLike.some(function(arg) { someCopy.push(arg); return false; });
|
||||
arraysEqual(someCopy, equivalentArray, \`\${reason}; some() copy\`);
|
||||
}`;
|
||||
eval(testArrayIteratorsSource);
|
||||
|
||||
function testDate() {
|
||||
// toGMTString is handled oddly in the engine. We don't bother to support
|
||||
// it over Xrays.
|
||||
@@ -507,6 +576,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
is(trickyArray[1], undefined, "Frozen length forbids new properties");
|
||||
|
||||
testTrickyObject(trickyArray);
|
||||
|
||||
testArrayIterators(new iwin.Array(1, 1, 2, 3, 5), [1, 1, 2, 3, 5]);
|
||||
}
|
||||
|
||||
// Parts of this function are kind of specific to testing Object, but we factor
|
||||
@@ -658,6 +729,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
||||
var xray = new iwin[c](0);
|
||||
var xrayTypedArrayProto = Object.getPrototypeOf(Object.getPrototypeOf(xray));
|
||||
testProtoCallables(inheritedCallables, new iwin[c](0), xrayTypedArrayProto, typedArrayProto);
|
||||
|
||||
// When testing iterators, make sure to do so from inside our web
|
||||
// extension sandbox, since from chrome we can't poke their indices. Note
|
||||
// that we have to actually recreate our functions that touch typed array
|
||||
// indices inside the sandbox, not just export them, because otherwise
|
||||
// they'll just run with our principal anyway.
|
||||
//
|
||||
// But we do want to export is(), since we want ours called.
|
||||
wesb.eval(arraysEqualSource);
|
||||
wesb.eval(testArrayIteratorsSource);
|
||||
Cu.exportFunction(is, wesb,
|
||||
{ defineAs: "is" });
|
||||
wesb.eval('testArrayIterators(t, [0, 0, 3, 0, 0, 0, 0, 0, 0, 0])');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,8 @@ this.PageThumbs = {
|
||||
*/
|
||||
getThumbnailURL: function PageThumbs_getThumbnailURL(aUrl) {
|
||||
return this.scheme + "://" + this.staticHost +
|
||||
"/?url=" + encodeURIComponent(aUrl);
|
||||
"/?url=" + encodeURIComponent(aUrl) +
|
||||
"&revision=" + PageThumbsStorage.getRevision(aUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -537,6 +538,44 @@ this.PageThumbsStorage = {
|
||||
return OS.Path.join(this.path, this.getLeafNameForURL(aURL));
|
||||
},
|
||||
|
||||
_revisionTable: {},
|
||||
|
||||
// Generate an arbitrary revision tag, i.e. one that can't be used to
|
||||
// infer URL frecency.
|
||||
_updateRevision(aURL) {
|
||||
// Initialize with a random value and increment on each update. Wrap around
|
||||
// modulo _revisionRange, so that even small values carry no meaning.
|
||||
let rev = this._revisionTable[aURL];
|
||||
if (rev == null)
|
||||
rev = Math.floor(Math.random() * this._revisionRange);
|
||||
this._revisionTable[aURL] = (rev + 1) % this._revisionRange;
|
||||
},
|
||||
|
||||
// If two thumbnails with the same URL and revision are in cache at the
|
||||
// same time, the image loader may pick the stale thumbnail in some cases.
|
||||
// Therefore _revisionRange must be large enough to prevent this, e.g.
|
||||
// in the pathological case image.cache.size (5MB by default) could fill
|
||||
// with (abnormally small) 10KB thumbnail images if the browser session
|
||||
// runs long enough (though this is unlikely as thumbnails are usually
|
||||
// only updated every MAX_THUMBNAIL_AGE_SECS).
|
||||
_revisionRange: 8192,
|
||||
|
||||
/**
|
||||
* Return a revision tag for the thumbnail stored for a given URL.
|
||||
*
|
||||
* @param aURL The URL spec string
|
||||
* @return A revision tag for the corresponding thumbnail. Returns a changed
|
||||
* value whenever the stored thumbnail changes.
|
||||
*/
|
||||
getRevision(aURL) {
|
||||
let rev = this._revisionTable[aURL];
|
||||
if (rev == null) {
|
||||
this._updateRevision(aURL);
|
||||
rev = this._revisionTable[aURL];
|
||||
}
|
||||
return rev;
|
||||
},
|
||||
|
||||
/**
|
||||
* Write the contents of a thumbnail, off the main thread.
|
||||
*
|
||||
@@ -566,7 +605,7 @@ this.PageThumbsStorage = {
|
||||
msg /*we don't want that message garbage-collected,
|
||||
as OS.Shared.Type.void_t.in_ptr.toMsg uses C-level
|
||||
memory tricks to enforce zero-copy*/).
|
||||
then(null, this._eatNoOverwriteError(aNoOverwrite));
|
||||
then(() => this._updateRevision(aURL), this._eatNoOverwriteError(aNoOverwrite));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -585,7 +624,7 @@ this.PageThumbsStorage = {
|
||||
let targetFile = this.getFilePathForURL(aTargetURL);
|
||||
let options = { noOverwrite: aNoOverwrite };
|
||||
return PageThumbsWorker.post("copy", [sourceFile, targetFile, options]).
|
||||
then(null, this._eatNoOverwriteError(aNoOverwrite));
|
||||
then(() => this._updateRevision(aTargetURL), this._eatNoOverwriteError(aNoOverwrite));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -696,7 +735,7 @@ this.PageThumbsStorage = {
|
||||
}
|
||||
};
|
||||
|
||||
let PageThumbsStorageMigrator = {
|
||||
var PageThumbsStorageMigrator = {
|
||||
get currentVersion() {
|
||||
try {
|
||||
return Services.prefs.getIntPref(PREF_STORAGE_VERSION);
|
||||
@@ -754,7 +793,7 @@ let PageThumbsStorageMigrator = {
|
||||
}
|
||||
};
|
||||
|
||||
let PageThumbsExpiration = {
|
||||
var PageThumbsExpiration = {
|
||||
_filters: [],
|
||||
|
||||
init: function Expiration_init() {
|
||||
@@ -819,12 +858,12 @@ let PageThumbsExpiration = {
|
||||
/**
|
||||
* Interface to a dedicated thread handling I/O
|
||||
*/
|
||||
let PageThumbsWorker = new BasePromiseWorker("resource://gre/modules/PageThumbsWorker.js");
|
||||
var PageThumbsWorker = new BasePromiseWorker("resource://gre/modules/PageThumbsWorker.js");
|
||||
// As the PageThumbsWorker performs I/O, we can receive instances of
|
||||
// OS.File.Error, so we need to install a decoder.
|
||||
PageThumbsWorker.ExceptionHandlers["OS.File.Error"] = OS.File.Error.fromMsg;
|
||||
|
||||
let PageThumbsHistoryObserver = {
|
||||
var PageThumbsHistoryObserver = {
|
||||
onDeleteURI: function Thumbnails_onDeleteURI(aURI, aGUID) {
|
||||
PageThumbsStorage.remove(aURI.spec);
|
||||
},
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
*
|
||||
* URL structure:
|
||||
*
|
||||
* moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F
|
||||
* moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F&revision=XX
|
||||
*
|
||||
* This URL requests an image for 'http://www.mozilla.org/'.
|
||||
* The value of the revision key may change when the stored thumbnail changes.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -88,11 +88,6 @@ function* capIfStaleErrorResponseUpdateTest() {
|
||||
yield addTab(URL);
|
||||
|
||||
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
|
||||
|
||||
// image cache entry timestamps have second resolution
|
||||
// so make sure the second part of this test takes part in a different second.
|
||||
yield wait(2000);
|
||||
|
||||
// update the thumbnail to be stale, then re-request it. The server will
|
||||
// return a 400 response and a red thumbnail.
|
||||
// The service should not save the thumbnail - so we (a) check the thumbnail
|
||||
@@ -124,11 +119,6 @@ function* capIfStaleGoodResponseUpdateTest() {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
|
||||
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
|
||||
|
||||
// image cache entry timestamps have second resolution
|
||||
// so make sure the second part of this test takes part in a different second.
|
||||
yield wait(2000);
|
||||
|
||||
// update the thumbnail to be stale, then re-request it. The server will
|
||||
// return a 200 response and a red thumbnail - so that new thumbnail should
|
||||
// end up captured.
|
||||
@@ -158,11 +148,6 @@ function* regularCapErrorResponseUpdateTest() {
|
||||
yield addTab(URL);
|
||||
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// image cache entry timestamps have second resolution
|
||||
// so make sure the second part of this test takes part in a different second.
|
||||
yield wait(2000);
|
||||
|
||||
// do it again - the server will return a 400, so the foreground service
|
||||
// should not update it.
|
||||
yield addTab(URL);
|
||||
@@ -177,11 +162,6 @@ function* regularCapGoodResponseUpdateTest() {
|
||||
yield addTab(URL);
|
||||
yield captureAndCheckColor(0, 255, 0, "we have a green thumbnail");
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// image cache entry timestamps have second resolution
|
||||
// so make sure the second part of this test takes part in a different second.
|
||||
yield wait(2000);
|
||||
|
||||
// do it again - the server will return a 200, so the foreground service
|
||||
// should update it.
|
||||
yield addTab(URL);
|
||||
|
||||
@@ -146,33 +146,21 @@ function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
|
||||
function retrieveImageDataForURL(aURL, aCallback) {
|
||||
let width = 100, height = 100;
|
||||
let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
|
||||
// create a tab with a chrome:// URL so it can host the thumbnail image.
|
||||
// Note that we tried creating the element directly in the top-level chrome
|
||||
// document, but this caused a strange problem:
|
||||
// * call this with the url of an image.
|
||||
// * immediately change the image content.
|
||||
// * call this again with the same url (now holding different content)
|
||||
// The original image data would be used. Maybe the img hadn't been
|
||||
// collected yet and the platform noticed the same URL, so reused the
|
||||
// content? Not sure - but this solves the problem.
|
||||
addTab("chrome://global/content/mozilla.xhtml", () => {
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let htmlns = "http://www.w3.org/1999/xhtml";
|
||||
let img = doc.createElementNS(htmlns, "img");
|
||||
img.setAttribute("src", thumb);
|
||||
|
||||
whenLoaded(img, function () {
|
||||
let canvas = document.createElementNS(htmlns, "canvas");
|
||||
canvas.setAttribute("width", width);
|
||||
canvas.setAttribute("height", height);
|
||||
let htmlns = "http://www.w3.org/1999/xhtml";
|
||||
let img = document.createElementNS(htmlns, "img");
|
||||
img.setAttribute("src", thumb);
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
let result = ctx.getImageData(0, 0, 100, 100).data;
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
aCallback(result);
|
||||
});
|
||||
whenLoaded(img, function () {
|
||||
let canvas = document.createElementNS(htmlns, "canvas");
|
||||
canvas.setAttribute("width", width);
|
||||
canvas.setAttribute("height", height);
|
||||
|
||||
// Draw the image to a canvas and compare the pixel color values.
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
let result = ctx.getImageData(0, 0, 100, 100).data;
|
||||
aCallback(result);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ if (Cu) {
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "FinalizationWitnessService",
|
||||
"@mozilla.org/toolkit/finalizationwitness;1",
|
||||
"nsIFinalizationWitnessService");
|
||||
|
||||
// For now, we're worried about add-ons using Promises with CPOWs, so we'll
|
||||
// permit them in this scope, but this support will go away soon.
|
||||
Cu.permitCPOWsInScope(this);
|
||||
}
|
||||
|
||||
const STATUS_PENDING = 0;
|
||||
|
||||
@@ -91,6 +91,10 @@ const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
// For now, we're worried about add-ons using Tasks with CPOWs, so we'll
|
||||
// permit them in this scope, but this support will go away soon.
|
||||
Cu.permitCPOWsInScope(this);
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
// The following error types are considered programmer errors, which should be
|
||||
|
||||
Reference in New Issue
Block a user