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:
2024-03-16 12:29:41 +08:00
parent 081721a2da
commit 904e3bdf3a
89 changed files with 1404 additions and 481 deletions
+6 -7
View File
@@ -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;
+1 -1
View File
@@ -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
+6 -2
View File
@@ -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
+8 -3
View File
@@ -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.
+10 -8
View File
@@ -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>
}
}
+2 -2
View File
@@ -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
+9 -22
View File
@@ -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;
+12 -22
View File
@@ -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;
}
}
+5 -11
View File
@@ -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
+2 -1
View File
@@ -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();
+5 -4
View File
@@ -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;
+14
View File
@@ -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()
{
+4 -14
View File
@@ -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
+7 -1
View File
@@ -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.
+1 -2
View File
@@ -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);
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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)
+7 -3
View File
@@ -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);
}
+1 -1
View File
@@ -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
+9 -5
View File
@@ -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);
+1 -1
View File
@@ -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();
+7 -1
View File
@@ -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;
}
+46
View File
@@ -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,
+16
View File
@@ -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,
+7 -1
View File
@@ -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");
+1 -1
View File
@@ -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);
+8
View File
@@ -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();
+8
View File
@@ -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);
+10
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+5 -1
View File
@@ -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;
}
+2 -2
View File
@@ -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;
+7
View File
@@ -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>
+7 -2
View File
@@ -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();
+15 -5
View File
@@ -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) {
+27 -1
View File
@@ -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,
+9
View File
@@ -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() {
+3 -2
View File
@@ -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());
}
+4
View File
@@ -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 {
+6 -1
View File
@@ -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.');
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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));
+13 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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;
+11 -3
View File
@@ -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
View File
@@ -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)
+4 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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();
+40 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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])');
}
}
+46 -7
View File
@@ -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);
+13 -25
View File
@@ -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);
});
}
+4
View File
@@ -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;
+4
View File
@@ -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