From d29f3f55602bcf51e37a4cea4e0c76a95220606e Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Mon, 31 Aug 2015 17:12:15 +0200 Subject: [PATCH 01/85] Bug 1199191 - firefox_ui_updates.py script should use 'abs_work_dir' as workspace folder. r=armenzg DONTBUILD --- testing/mozharness/scripts/firefox_ui_updates.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/mozharness/scripts/firefox_ui_updates.py b/testing/mozharness/scripts/firefox_ui_updates.py index 8947693e1d7..5133232d1a8 100755 --- a/testing/mozharness/scripts/firefox_ui_updates.py +++ b/testing/mozharness/scripts/firefox_ui_updates.py @@ -310,6 +310,8 @@ class FirefoxUIUpdates(FirefoxUITests): # Log to stdout until tests are stable. '--gecko-log=-', '--address=localhost:%s' % marionette_port, + # Use the work dir to get temporary data stored + '--workspace=%s' % dirs['abs_work_dir'], ] if symbols_url: From 7e49ec39f65a7fd3faabe108a6bf3ed92ba72cb6 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 02/85] Bug 895274 part.39 Rename NS_POINTER_EVENT_START to ePointerEventFirst and add ePointerEventLast r=smaug --- dom/events/EventListenerManager.cpp | 4 ++-- widget/EventMessageList.h | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 2cbaff8c701..f5f85a5b32e 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -352,8 +352,8 @@ EventListenerManager::AddEventListenerInternal( if (window && !aFlags.mInSystemGroup) { window->SetHasTouchEventListeners(); } - } else if (aEventMessage >= NS_POINTER_EVENT_START && - aEventMessage <= NS_POINTER_LOST_CAPTURE) { + } else if (aEventMessage >= ePointerEventFirst && + aEventMessage <= ePointerEventLast) { nsPIDOMWindow* window = GetInnerWindowForTarget(); if (aTypeAtom == nsGkAtoms::onpointerenter || aTypeAtom == nsGkAtoms::onpointerleave) { diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 8aad5ec80b2..115f6e7cc33 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -63,17 +63,18 @@ NS_EVENT_MESSAGE(eMouseLeave, eMouseEventFirst + 35) NS_EVENT_MESSAGE(eMouseLongTap, eMouseEventFirst + 36) // Pointer spec events -NS_EVENT_MESSAGE(NS_POINTER_EVENT_START, 4400) -NS_EVENT_MESSAGE(NS_POINTER_MOVE, NS_POINTER_EVENT_START) -NS_EVENT_MESSAGE(NS_POINTER_UP, NS_POINTER_EVENT_START + 1) -NS_EVENT_MESSAGE(NS_POINTER_DOWN, NS_POINTER_EVENT_START + 2) -NS_EVENT_MESSAGE(NS_POINTER_OVER, NS_POINTER_EVENT_START + 22) -NS_EVENT_MESSAGE(NS_POINTER_OUT, NS_POINTER_EVENT_START + 23) -NS_EVENT_MESSAGE(NS_POINTER_ENTER, NS_POINTER_EVENT_START + 24) -NS_EVENT_MESSAGE(NS_POINTER_LEAVE, NS_POINTER_EVENT_START + 25) -NS_EVENT_MESSAGE(NS_POINTER_CANCEL, NS_POINTER_EVENT_START + 26) -NS_EVENT_MESSAGE(NS_POINTER_GOT_CAPTURE, NS_POINTER_EVENT_START + 27) -NS_EVENT_MESSAGE(NS_POINTER_LOST_CAPTURE, NS_POINTER_EVENT_START + 28) +NS_EVENT_MESSAGE(ePointerEventFirst, 4400) +NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) +NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) +NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) +NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) +NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) +NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) +NS_EVENT_MESSAGE(NS_POINTER_LEAVE, ePointerEventFirst + 25) +NS_EVENT_MESSAGE(NS_POINTER_CANCEL, ePointerEventFirst + 26) +NS_EVENT_MESSAGE(NS_POINTER_GOT_CAPTURE, ePointerEventFirst + 27) +NS_EVENT_MESSAGE(NS_POINTER_LOST_CAPTURE, ePointerEventFirst + 28) +NS_EVENT_MESSAGE(ePointerEventLast, NS_POINTER_LOST_CAPTURE) NS_EVENT_MESSAGE(NS_CONTEXTMENU_MESSAGE_START, 500) NS_EVENT_MESSAGE(NS_CONTEXTMENU, NS_CONTEXTMENU_MESSAGE_START) From 494fccf4111b48e64cea2946777328f5400c20ed Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 03/85] Bug 895274 part.40 Rename NS_POINTER_LOST_CAPTURE to ePointerLostCapture r=smaug --- dom/events/EventNameList.h | 2 +- widget/EventMessageList.h | 4 ++-- widget/MouseEvents.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index f13f33d352c..6205adfa95d 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -347,7 +347,7 @@ EVENT(gotpointercapture, EventNameType_All, ePointerEventClass) EVENT(lostpointercapture, - NS_POINTER_LOST_CAPTURE, + ePointerLostCapture, EventNameType_All, ePointerEventClass) diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 115f6e7cc33..72e5a7009f8 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -73,8 +73,8 @@ NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) NS_EVENT_MESSAGE(NS_POINTER_LEAVE, ePointerEventFirst + 25) NS_EVENT_MESSAGE(NS_POINTER_CANCEL, ePointerEventFirst + 26) NS_EVENT_MESSAGE(NS_POINTER_GOT_CAPTURE, ePointerEventFirst + 27) -NS_EVENT_MESSAGE(NS_POINTER_LOST_CAPTURE, ePointerEventFirst + 28) -NS_EVENT_MESSAGE(ePointerEventLast, NS_POINTER_LOST_CAPTURE) +NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) +NS_EVENT_MESSAGE(ePointerEventLast, ePointerLostCapture) NS_EVENT_MESSAGE(NS_CONTEXTMENU_MESSAGE_START, 500) NS_EVENT_MESSAGE(NS_CONTEXTMENU, NS_CONTEXTMENU_MESSAGE_START) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 6d2a98dd54f..62a4121c732 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -632,7 +632,7 @@ public: break; case NS_POINTER_CANCEL: case NS_POINTER_GOT_CAPTURE: - case NS_POINTER_LOST_CAPTURE: + case ePointerLostCapture: mFlags.mCancelable = false; break; default: From 4e925becf6051b3e36c60e9a2a227a8d1f8ce799 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 04/85] Bug 895274 part.41 Rename NS_POINTER_GOT_CAPTURE to ePointerGotCapture r=smaug --- dom/events/EventNameList.h | 2 +- widget/EventMessageList.h | 2 +- widget/MouseEvents.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 6205adfa95d..129dedf961b 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -343,7 +343,7 @@ EVENT(pointerleave, EventNameType_All, ePointerEventClass) EVENT(gotpointercapture, - NS_POINTER_GOT_CAPTURE, + ePointerGotCapture, EventNameType_All, ePointerEventClass) EVENT(lostpointercapture, diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 72e5a7009f8..aa6ca4e94d0 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -72,7 +72,7 @@ NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) NS_EVENT_MESSAGE(NS_POINTER_LEAVE, ePointerEventFirst + 25) NS_EVENT_MESSAGE(NS_POINTER_CANCEL, ePointerEventFirst + 26) -NS_EVENT_MESSAGE(NS_POINTER_GOT_CAPTURE, ePointerEventFirst + 27) +NS_EVENT_MESSAGE(ePointerGotCapture, ePointerEventFirst + 27) NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) NS_EVENT_MESSAGE(ePointerEventLast, ePointerLostCapture) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 62a4121c732..9fde3dbb7e8 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -631,7 +631,7 @@ public: mFlags.mCancelable = false; break; case NS_POINTER_CANCEL: - case NS_POINTER_GOT_CAPTURE: + case ePointerGotCapture: case ePointerLostCapture: mFlags.mCancelable = false; break; From e9a2d47af354fbf114d18012a5b007e89f984180 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 05/85] Bug 895274 part.42 Rename NS_POINTER_CANCEL to ePointerCancel r=smaug --- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 4 ++-- layout/base/nsPresShell.cpp | 7 ++++--- widget/EventMessageList.h | 2 +- widget/MouseEvents.h | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 129dedf961b..b69fd51ece6 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -323,7 +323,7 @@ EVENT(pointerup, EventNameType_All, ePointerEventClass) EVENT(pointercancel, - NS_POINTER_CANCEL, + ePointerCancel, EventNameType_All, ePointerEventClass) EVENT(pointerover, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 3da445f354e..929d4858169 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3001,7 +3001,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, SetActiveManager(this, activeContent); } break; - case NS_POINTER_CANCEL: { + case ePointerCancel: { if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { GenerateMouseEnterExit(mouseEvent); } @@ -4185,7 +4185,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) } break; case NS_POINTER_LEAVE: - case NS_POINTER_CANCEL: + case ePointerCancel: case eMouseExitFromWidget: { // This is actually the window mouse exit or pointer leave event. We're not moving diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index bca0a871051..16fb1cae0ce 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6707,7 +6707,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, pointerMessage = NS_POINTER_DOWN; break; case NS_TOUCH_CANCEL: - pointerMessage = NS_POINTER_CANCEL; + pointerMessage = ePointerCancel; break; default: return NS_OK; @@ -7466,9 +7466,10 @@ PresShell::HandleEvent(nsIFrame* aFrame, } if (pointerEvent->mMessage == NS_POINTER_UP || - pointerEvent->mMessage == NS_POINTER_CANCEL) { + pointerEvent->mMessage == ePointerCancel) { // Implicitly releasing capture for given pointer. - // LOST_POINTER_CAPTURE should be send after NS_POINTER_UP or NS_POINTER_CANCEL. + // ePointerLostCapture should be send after NS_POINTER_UP or + // ePointerCancel. releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent); } } diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index aa6ca4e94d0..611e5b4c346 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -71,7 +71,7 @@ NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) NS_EVENT_MESSAGE(NS_POINTER_LEAVE, ePointerEventFirst + 25) -NS_EVENT_MESSAGE(NS_POINTER_CANCEL, ePointerEventFirst + 26) +NS_EVENT_MESSAGE(ePointerCancel, ePointerEventFirst + 26) NS_EVENT_MESSAGE(ePointerGotCapture, ePointerEventFirst + 27) NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) NS_EVENT_MESSAGE(ePointerEventLast, ePointerLostCapture) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 9fde3dbb7e8..9f858b59df2 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -630,7 +630,7 @@ public: mFlags.mBubbles = false; mFlags.mCancelable = false; break; - case NS_POINTER_CANCEL: + case ePointerCancel: case ePointerGotCapture: case ePointerLostCapture: mFlags.mCancelable = false; From 8573b1888d5af77ea7e8ebaeb84eb8409b7b4b7d Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 06/85] Bug 895274 part.43 Rename NS_POINTER_LEAVE to ePointerLeave r=smaug --- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 8 ++++---- widget/EventMessageList.h | 2 +- widget/MouseEvents.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index b69fd51ece6..086da4b3112 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -339,7 +339,7 @@ EVENT(pointerenter, EventNameType_All, ePointerEventClass) EVENT(pointerleave, - NS_POINTER_LEAVE, + ePointerLeave, EventNameType_All, ePointerEventClass) EVENT(gotpointercapture, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 929d4858169..428eaed39e0 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -622,7 +622,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, } else { if (sPointerEventEnabled) { // We should synthetize corresponding pointer events - GeneratePointerEnterExit(NS_POINTER_LEAVE, mouseEvent); + GeneratePointerEnterExit(ePointerLeave, mouseEvent); } GenerateMouseEnterExit(mouseEvent); //This is a window level mouse exit event and should stop here @@ -3944,14 +3944,14 @@ EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, } // In case we go out from capturing element (retargetedByPointerCapture is true) - // we should dispatch NS_POINTER_LEAVE event and only for capturing element. + // we should dispatch ePointerLeave event and only for capturing element. nsRefPtr movingInto = aMouseEvent->retargetedByPointerCapture ? wrapper->mLastOverElement->GetParent() : aMovingInto; EnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement, movingInto, aMouseEvent, - isPointer ? NS_POINTER_LEAVE : eMouseLeave); + isPointer ? ePointerLeave : eMouseLeave); // Fire mouseout DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? NS_POINTER_OUT : eMouseOut, @@ -4184,7 +4184,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) } } break; - case NS_POINTER_LEAVE: + case ePointerLeave: case ePointerCancel: case eMouseExitFromWidget: { diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 611e5b4c346..2ca0ca48142 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -70,7 +70,7 @@ NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) -NS_EVENT_MESSAGE(NS_POINTER_LEAVE, ePointerEventFirst + 25) +NS_EVENT_MESSAGE(ePointerLeave, ePointerEventFirst + 25) NS_EVENT_MESSAGE(ePointerCancel, ePointerEventFirst + 26) NS_EVENT_MESSAGE(ePointerGotCapture, ePointerEventFirst + 27) NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 9f858b59df2..0186f1c6eaa 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -626,7 +626,7 @@ public: { switch (mMessage) { case NS_POINTER_ENTER: - case NS_POINTER_LEAVE: + case ePointerLeave: mFlags.mBubbles = false; mFlags.mCancelable = false; break; From d9f9d54b87bc7d226edec3f9e76de9da710d9a78 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 07/85] Bug 895274 part.44 Rename NS_POINTER_ENTER to ePointerEnter r=smaug --- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 5 ++--- widget/EventMessageList.h | 2 +- widget/MouseEvents.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 086da4b3112..3db4f279d50 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -335,7 +335,7 @@ EVENT(pointerout, EventNameType_All, ePointerEventClass) EVENT(pointerenter, - NS_POINTER_ENTER, + ePointerEnter, EventNameType_All, ePointerEventClass) EVENT(pointerleave, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 428eaed39e0..9cf77877e55 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3872,8 +3872,7 @@ public: ~EnterLeaveDispatcher() { - if (mEventMessage == eMouseEnter || - mEventMessage == NS_POINTER_ENTER) { + if (mEventMessage == eMouseEnter || mEventMessage == ePointerEnter) { for (int32_t i = mTargets.Count() - 1; i >= 0; --i) { mESM->DispatchMouseOrPointerEvent(mMouseEvent, mEventMessage, mTargets[i], mRelatedTarget); @@ -4010,7 +4009,7 @@ EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, Maybe enterDispatcher; if (dispatch) { enterDispatcher.emplace(this, aContent, lastOverElement, aMouseEvent, - isPointer ? NS_POINTER_ENTER : eMouseEnter); + isPointer ? ePointerEnter : eMouseEnter); } NotifyMouseOut(aMouseEvent, aContent); diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 2ca0ca48142..8c4256c1deb 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -69,7 +69,7 @@ NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) -NS_EVENT_MESSAGE(NS_POINTER_ENTER, ePointerEventFirst + 24) +NS_EVENT_MESSAGE(ePointerEnter, ePointerEventFirst + 24) NS_EVENT_MESSAGE(ePointerLeave, ePointerEventFirst + 25) NS_EVENT_MESSAGE(ePointerCancel, ePointerEventFirst + 26) NS_EVENT_MESSAGE(ePointerGotCapture, ePointerEventFirst + 27) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 0186f1c6eaa..7d9ac0b3838 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -625,7 +625,7 @@ public: void UpdateFlags() { switch (mMessage) { - case NS_POINTER_ENTER: + case ePointerEnter: case ePointerLeave: mFlags.mBubbles = false; mFlags.mCancelable = false; From 6743e9ddde1d0583e1900443d7dac55f422046ae Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 08/85] Bug 895274 part.45 Rename NS_POINTER_OUT to ePointerOut r=smaug --- dom/base/FragmentOrElement.cpp | 2 +- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 2 +- widget/EventMessageList.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 28043916424..989a6199361 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -680,7 +680,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) if ((aVisitor.mEvent->mMessage == eMouseOver || aVisitor.mEvent->mMessage == eMouseOut || aVisitor.mEvent->mMessage == NS_POINTER_OVER || - aVisitor.mEvent->mMessage == NS_POINTER_OUT) && + aVisitor.mEvent->mMessage == ePointerOut) && // Check if we should stop event propagation when event has just been // dispatched or when we're about to propagate from // chrome access only subtree or if we are about to propagate out of diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 644be126ee3..c325ebb1532 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -710,7 +710,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, } else if (aType.EqualsLiteral("pointerover")) { msg = NS_POINTER_OVER; } else if (aType.EqualsLiteral("pointerout")) { - msg = NS_POINTER_OUT; + msg = ePointerOut; } else { return NS_ERROR_FAILURE; } diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 3db4f279d50..28bdc9e4e44 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -331,7 +331,7 @@ EVENT(pointerover, EventNameType_All, ePointerEventClass) EVENT(pointerout, - NS_POINTER_OUT, + ePointerOut, EventNameType_All, ePointerEventClass) EVENT(pointerenter, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 9cf77877e55..33383e1c78f 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3953,7 +3953,7 @@ EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, isPointer ? ePointerLeave : eMouseLeave); // Fire mouseout - DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? NS_POINTER_OUT : eMouseOut, + DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? ePointerOut : eMouseOut, wrapper->mLastOverElement, aMovingInto); wrapper->mLastOverFrame = nullptr; diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 8c4256c1deb..cf181f66849 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -68,7 +68,7 @@ NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) -NS_EVENT_MESSAGE(NS_POINTER_OUT, ePointerEventFirst + 23) +NS_EVENT_MESSAGE(ePointerOut, ePointerEventFirst + 23) NS_EVENT_MESSAGE(ePointerEnter, ePointerEventFirst + 24) NS_EVENT_MESSAGE(ePointerLeave, ePointerEventFirst + 25) NS_EVENT_MESSAGE(ePointerCancel, ePointerEventFirst + 26) From 99decd7117e12dc98a5d04b34e0d9eb19882aadf Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:05 +0900 Subject: [PATCH 09/85] Bug 895274 part.46 Rename NS_POINTER_OVER to ePointerOver r=smaug --- dom/base/FragmentOrElement.cpp | 2 +- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 2 +- widget/EventMessageList.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 989a6199361..1cb8b5115d1 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -679,7 +679,7 @@ nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); if ((aVisitor.mEvent->mMessage == eMouseOver || aVisitor.mEvent->mMessage == eMouseOut || - aVisitor.mEvent->mMessage == NS_POINTER_OVER || + aVisitor.mEvent->mMessage == ePointerOver || aVisitor.mEvent->mMessage == ePointerOut) && // Check if we should stop event propagation when event has just been // dispatched or when we're about to propagate from diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index c325ebb1532..eebd6a4b136 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -708,7 +708,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, } else if (aType.EqualsLiteral("pointermove")) { msg = NS_POINTER_MOVE; } else if (aType.EqualsLiteral("pointerover")) { - msg = NS_POINTER_OVER; + msg = ePointerOver; } else if (aType.EqualsLiteral("pointerout")) { msg = ePointerOut; } else { diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 28bdc9e4e44..8fdd6717a9a 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -327,7 +327,7 @@ EVENT(pointercancel, EventNameType_All, ePointerEventClass) EVENT(pointerover, - NS_POINTER_OVER, + ePointerOver, EventNameType_All, ePointerEventClass) EVENT(pointerout, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 33383e1c78f..d638f50e474 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -4026,7 +4026,7 @@ EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, // Fire mouseover wrapper->mLastOverFrame = DispatchMouseOrPointerEvent(aMouseEvent, - isPointer ? NS_POINTER_OVER : eMouseOver, + isPointer ? ePointerOver : eMouseOver, aContent, lastOverElement); wrapper->mLastOverElement = aContent; } else { diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index cf181f66849..306326dc713 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -67,7 +67,7 @@ NS_EVENT_MESSAGE(ePointerEventFirst, 4400) NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) -NS_EVENT_MESSAGE(NS_POINTER_OVER, ePointerEventFirst + 22) +NS_EVENT_MESSAGE(ePointerOver, ePointerEventFirst + 22) NS_EVENT_MESSAGE(ePointerOut, ePointerEventFirst + 23) NS_EVENT_MESSAGE(ePointerEnter, ePointerEventFirst + 24) NS_EVENT_MESSAGE(ePointerLeave, ePointerEventFirst + 25) From a54fa95ae96527a74eb80b2092b99c3ac05aae82 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:06 +0900 Subject: [PATCH 10/85] Bug 895274 part.47 Rename NS_POINTER_DOWN to ePointerDown r=smaug --- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 4 ++-- layout/base/nsPresShell.cpp | 8 ++++---- widget/EventMessageList.h | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index eebd6a4b136..288a75fe498 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -702,7 +702,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, EventMessage msg; if (aType.EqualsLiteral("pointerdown")) { - msg = NS_POINTER_DOWN; + msg = ePointerDown; } else if (aType.EqualsLiteral("pointerup")) { msg = NS_POINTER_UP; } else if (aType.EqualsLiteral("pointermove")) { diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 8fdd6717a9a..19195dc4cbc 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -311,7 +311,7 @@ EVENT(mozpointerlockerror, EventNameType_HTML, eBasicEventClass) EVENT(pointerdown, - NS_POINTER_DOWN, + ePointerDown, EventNameType_All, ePointerEventClass) EVENT(pointermove, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index d638f50e474..feeab9fec8e 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -630,7 +630,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, break; } case eMouseMove: - case NS_POINTER_DOWN: + case ePointerDown: case NS_POINTER_MOVE: { // on the Mac, GenerateDragGesture() may not return until the drag // has completed and so |aTargetFrame| may have been deleted (moving @@ -4149,7 +4149,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) } case NS_POINTER_MOVE: - case NS_POINTER_DOWN: + case ePointerDown: { // Get the target content target (mousemove target == mouseover target) nsCOMPtr targetElement = GetEventTargetContent(aMouseEvent); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 16fb1cae0ce..ec7fd796cb5 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6351,7 +6351,7 @@ PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent) new PointerInfo(false, mouseEvent->inputSource, true)); } break; - case NS_POINTER_DOWN: + case ePointerDown: // In this case we switch pointer to active state if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { gActivePointersIds->Put(pointerEvent->pointerId, @@ -6678,7 +6678,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, pointerMessage = NS_POINTER_UP; break; case eMouseDown: - pointerMessage = NS_POINTER_DOWN; + pointerMessage = ePointerDown; break; default: return NS_OK; @@ -6704,7 +6704,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, pointerMessage = NS_POINTER_UP; break; case NS_TOUCH_START: - pointerMessage = NS_POINTER_DOWN; + pointerMessage = ePointerDown; break; case NS_TOUCH_CANCEL: pointerMessage = ePointerCancel; @@ -7450,7 +7450,7 @@ PresShell::HandleEvent(nsIFrame* aFrame, } if (aEvent->mClass == ePointerEventClass && - aEvent->mMessage != NS_POINTER_DOWN) { + aEvent->mMessage != ePointerDown) { if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { uint32_t pointerId = pointerEvent->pointerId; nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId); diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 306326dc713..66ae622d2e9 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -66,7 +66,7 @@ NS_EVENT_MESSAGE(eMouseLongTap, eMouseEventFirst + 36) NS_EVENT_MESSAGE(ePointerEventFirst, 4400) NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) -NS_EVENT_MESSAGE(NS_POINTER_DOWN, ePointerEventFirst + 2) +NS_EVENT_MESSAGE(ePointerDown, ePointerEventFirst + 2) NS_EVENT_MESSAGE(ePointerOver, ePointerEventFirst + 22) NS_EVENT_MESSAGE(ePointerOut, ePointerEventFirst + 23) NS_EVENT_MESSAGE(ePointerEnter, ePointerEventFirst + 24) From 7078b2407c6af8968f5a1af06e72746a5d4d4322 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:06 +0900 Subject: [PATCH 11/85] Bug 895274 part.48 Rename NS_POINTER_UP to ePointerUp r=smaug --- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 4 ++-- layout/base/nsPresShell.cpp | 10 +++++----- widget/EventMessageList.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 288a75fe498..255dcb0e85f 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -704,7 +704,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, if (aType.EqualsLiteral("pointerdown")) { msg = ePointerDown; } else if (aType.EqualsLiteral("pointerup")) { - msg = NS_POINTER_UP; + msg = ePointerUp; } else if (aType.EqualsLiteral("pointermove")) { msg = NS_POINTER_MOVE; } else if (aType.EqualsLiteral("pointerover")) { diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 19195dc4cbc..870d2246b6f 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -319,7 +319,7 @@ EVENT(pointermove, EventNameType_All, ePointerEventClass) EVENT(pointerup, - NS_POINTER_UP, + ePointerUp, EventNameType_All, ePointerEventClass) EVENT(pointercancel, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index feeab9fec8e..5541eeeba94 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3008,7 +3008,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext, // This break was commented specially // break; } - case NS_POINTER_UP: { + case ePointerUp: { WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); // After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table // Mouse/Pen pointers are valid all the time (not only between down/up) @@ -4164,7 +4164,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) } } break; - case NS_POINTER_UP: + case ePointerUp: { // Get the target content target (mousemove target == mouseover target) nsCOMPtr targetElement = GetEventTargetContent(aMouseEvent); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ec7fd796cb5..fa2188edea7 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6358,7 +6358,7 @@ PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent) new PointerInfo(true, pointerEvent->inputSource, pointerEvent->isPrimary)); } break; - case NS_POINTER_UP: + case ePointerUp: // In this case we remove information about pointer or turn off active state if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) { @@ -6675,7 +6675,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, pointerMessage = NS_POINTER_MOVE; break; case eMouseUp: - pointerMessage = NS_POINTER_UP; + pointerMessage = ePointerUp; break; case eMouseDown: pointerMessage = ePointerDown; @@ -6701,7 +6701,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, pointerMessage = NS_POINTER_MOVE; break; case NS_TOUCH_END: - pointerMessage = NS_POINTER_UP; + pointerMessage = ePointerUp; break; case NS_TOUCH_START: pointerMessage = ePointerDown; @@ -7465,10 +7465,10 @@ PresShell::HandleEvent(nsIFrame* aFrame, frame = capturingFrame; } - if (pointerEvent->mMessage == NS_POINTER_UP || + if (pointerEvent->mMessage == ePointerUp || pointerEvent->mMessage == ePointerCancel) { // Implicitly releasing capture for given pointer. - // ePointerLostCapture should be send after NS_POINTER_UP or + // ePointerLostCapture should be send after ePointerUp or // ePointerCancel. releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent); } diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 66ae622d2e9..ad64298a8d9 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -65,7 +65,7 @@ NS_EVENT_MESSAGE(eMouseLongTap, eMouseEventFirst + 36) // Pointer spec events NS_EVENT_MESSAGE(ePointerEventFirst, 4400) NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) -NS_EVENT_MESSAGE(NS_POINTER_UP, ePointerEventFirst + 1) +NS_EVENT_MESSAGE(ePointerUp, ePointerEventFirst + 1) NS_EVENT_MESSAGE(ePointerDown, ePointerEventFirst + 2) NS_EVENT_MESSAGE(ePointerOver, ePointerEventFirst + 22) NS_EVENT_MESSAGE(ePointerOut, ePointerEventFirst + 23) From fe692ad11f4fd3cd5a5ddf51c6a73228f0582931 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:06 +0900 Subject: [PATCH 12/85] Bug 895274 part.49 Rename NS_POINTER_MOVE to ePointerMove r=smaug --- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 4 ++-- layout/base/nsPresShell.cpp | 4 ++-- widget/EventMessageList.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 255dcb0e85f..bc65e3039b6 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -706,7 +706,7 @@ nsDOMWindowUtils::SendPointerEventCommon(const nsAString& aType, } else if (aType.EqualsLiteral("pointerup")) { msg = ePointerUp; } else if (aType.EqualsLiteral("pointermove")) { - msg = NS_POINTER_MOVE; + msg = ePointerMove; } else if (aType.EqualsLiteral("pointerover")) { msg = ePointerOver; } else if (aType.EqualsLiteral("pointerout")) { diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 870d2246b6f..3803ea83c6c 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -315,7 +315,7 @@ EVENT(pointerdown, EventNameType_All, ePointerEventClass) EVENT(pointermove, - NS_POINTER_MOVE, + ePointerMove, EventNameType_All, ePointerEventClass) EVENT(pointerup, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 5541eeeba94..9f7d6eff509 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -631,7 +631,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, } case eMouseMove: case ePointerDown: - case NS_POINTER_MOVE: { + case ePointerMove: { // on the Mac, GenerateDragGesture() may not return until the drag // has completed and so |aTargetFrame| may have been deleted (moving // a bookmark, for example). If this is the case, however, we know @@ -4148,7 +4148,7 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) sLastRefPoint = aMouseEvent->refPoint; } - case NS_POINTER_MOVE: + case ePointerMove: case ePointerDown: { // Get the target content target (mousemove target == mouseover target) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index fa2188edea7..19b5de01b79 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6672,7 +6672,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, if (mouseEvent->buttons == 0) { button = -1; } - pointerMessage = NS_POINTER_MOVE; + pointerMessage = ePointerMove; break; case eMouseUp: pointerMessage = ePointerUp; @@ -6698,7 +6698,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, // copy the event switch (touchEvent->mMessage) { case NS_TOUCH_MOVE: - pointerMessage = NS_POINTER_MOVE; + pointerMessage = ePointerMove; break; case NS_TOUCH_END: pointerMessage = ePointerUp; diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index ad64298a8d9..4329d4a2271 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -64,7 +64,7 @@ NS_EVENT_MESSAGE(eMouseLongTap, eMouseEventFirst + 36) // Pointer spec events NS_EVENT_MESSAGE(ePointerEventFirst, 4400) -NS_EVENT_MESSAGE(NS_POINTER_MOVE, ePointerEventFirst) +NS_EVENT_MESSAGE(ePointerMove, ePointerEventFirst) NS_EVENT_MESSAGE(ePointerUp, ePointerEventFirst + 1) NS_EVENT_MESSAGE(ePointerDown, ePointerEventFirst + 2) NS_EVENT_MESSAGE(ePointerOver, ePointerEventFirst + 22) From ea7838685f0465a811731a5d50a70351022d75b1 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:06 +0900 Subject: [PATCH 13/85] Bug 895274 part.50 Rename NS_CONTEXTMENU_MESSAGE_START to eContextMenuFirst r=smaug --- widget/EventMessageList.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 4329d4a2271..45a358f4b50 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -76,8 +76,8 @@ NS_EVENT_MESSAGE(ePointerGotCapture, ePointerEventFirst + 27) NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) NS_EVENT_MESSAGE(ePointerEventLast, ePointerLostCapture) -NS_EVENT_MESSAGE(NS_CONTEXTMENU_MESSAGE_START, 500) -NS_EVENT_MESSAGE(NS_CONTEXTMENU, NS_CONTEXTMENU_MESSAGE_START) +NS_EVENT_MESSAGE(eContextMenuFirst, 500) +NS_EVENT_MESSAGE(NS_CONTEXTMENU, eContextMenuFirst) NS_EVENT_MESSAGE(NS_STREAM_EVENT_START, 1100) NS_EVENT_MESSAGE(NS_LOAD, NS_STREAM_EVENT_START) From 841b1687ebe5251f0d9374cdee1b53ccc6030cb4 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 1 Sep 2015 00:20:06 +0900 Subject: [PATCH 14/85] Bug 895274 part.51 Rename NS_CONTEXTMENU to eContextMenu r=smaug --- dom/base/nsContentUtils.cpp | 2 +- dom/events/EventNameList.h | 2 +- dom/events/EventStateManager.cpp | 8 ++++---- dom/events/WheelHandlingHelper.cpp | 2 +- dom/xul/nsXULElement.cpp | 2 +- editor/libeditor/nsHTMLEditorEventListener.cpp | 2 +- gfx/layers/apz/src/APZCTreeManager.cpp | 2 +- layout/base/nsPresShell.cpp | 2 +- layout/xul/nsMenuFrame.cpp | 2 +- widget/EventMessageList.h | 2 +- widget/MouseEvents.h | 8 ++++---- widget/android/nsWindow.cpp | 2 +- widget/cocoa/nsChildView.mm | 2 +- widget/gtk/nsWindow.cpp | 4 ++-- widget/qt/nsWindow.cpp | 4 ++-- widget/windows/nsWindow.cpp | 4 ++-- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index dd4ccf0f2ee..0b0e1e66d9b 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7848,7 +7848,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr aPresShell, else if (aType.EqualsLiteral("mouseout")) msg = eMouseExitFromWidget; else if (aType.EqualsLiteral("contextmenu")) { - msg = NS_CONTEXTMENU; + msg = eContextMenu; contextMenuKey = (aButton == 0); } else if (aType.EqualsLiteral("MozMouseHittest")) msg = eMouseHitTest; diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 3803ea83c6c..f9cffff795c 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -169,7 +169,7 @@ EVENT(click, EventNameType_All, eMouseEventClass) EVENT(contextmenu, - NS_CONTEXTMENU, + eContextMenu, EventNameType_HTMLXUL, eMouseEventClass) // Not supported yet diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 9f7d6eff509..5a860c9a24a 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -543,7 +543,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, *aStatus = nsEventStatus_eIgnore; switch (aEvent->mMessage) { - case NS_CONTEXTMENU: + case eContextMenu: if (sIsPointerLocked) { return NS_ERROR_DOM_INVALID_STATE_ERR; } @@ -1174,7 +1174,7 @@ CrossProcessSafeEvent(const WidgetEvent& aEvent) case eMouseDown: case eMouseUp: case eMouseMove: - case NS_CONTEXTMENU: + case eContextMenu: case eMouseEnterIntoWidget: case eMouseExitFromWidget: return true; @@ -1452,7 +1452,7 @@ EventStateManager::FireContextClick() if (allowedToDispatch) { // init the event while mCurrentTarget is still good - WidgetMouseEvent event(true, NS_CONTEXTMENU, targetWidget, + WidgetMouseEvent event(true, eContextMenu, targetWidget, WidgetMouseEvent::eReal); event.clickCount = 1; FillInEventFromGestureDown(&event); @@ -1478,7 +1478,7 @@ EventStateManager::FireContextClick() nullptr, &status); // We don't need to dispatch to frame handling because no frames - // watch NS_CONTEXTMENU except for nsMenuFrame and that's only for + // watch eContextMenu except for nsMenuFrame and that's only for // dismissal. That's just as well since we don't really know // which frame to send it to. } diff --git a/dom/events/WheelHandlingHelper.cpp b/dom/events/WheelHandlingHelper.cpp index f16eb4c9751..09010b6bbb2 100644 --- a/dom/events/WheelHandlingHelper.cpp +++ b/dom/events/WheelHandlingHelper.cpp @@ -214,7 +214,7 @@ WheelTransaction::OnEvent(WidgetEvent* aEvent) case eMouseDown: case eMouseDoubleClick: case eMouseClick: - case NS_CONTEXTMENU: + case eContextMenu: case NS_DRAGDROP_DROP: EndTransaction(); return; diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 32c285b9a34..7ae3278012f 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -1268,7 +1268,7 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) (aVisitor.mEvent->mMessage == eMouseClick || aVisitor.mEvent->mMessage == eMouseDoubleClick || aVisitor.mEvent->mMessage == NS_XUL_COMMAND || - aVisitor.mEvent->mMessage == NS_CONTEXTMENU || + aVisitor.mEvent->mMessage == eContextMenu || aVisitor.mEvent->mMessage == NS_DRAGDROP_START || aVisitor.mEvent->mMessage == NS_DRAGDROP_GESTURE)) { // Don't propagate these events from native anonymous scrollbar. diff --git a/editor/libeditor/nsHTMLEditorEventListener.cpp b/editor/libeditor/nsHTMLEditorEventListener.cpp index 8c1fd125139..86ebfe18783 100644 --- a/editor/libeditor/nsHTMLEditorEventListener.cpp +++ b/editor/libeditor/nsHTMLEditorEventListener.cpp @@ -90,7 +90,7 @@ nsHTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent) // Detect only "context menu" click // XXX This should be easier to do! - // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event + // But eDOMEvents_contextmenu and eContextMenu is not exposed in any event // interface :-( int16_t buttonNumber; nsresult rv = aMouseEvent->GetButton(&buttonNumber); diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 4c48464c9f1..7c54322fc1b 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -893,7 +893,7 @@ APZCTreeManager::UpdateWheelTransaction(WidgetInputEvent& aEvent) case eMouseDown: case eMouseDoubleClick: case eMouseClick: - case NS_CONTEXTMENU: + case eContextMenu: case NS_DRAGDROP_DROP: txn->EndTransaction(); return; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 19b5de01b79..dbeaf9e01fe 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -7914,7 +7914,7 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus) } } - if (aEvent->mMessage == NS_CONTEXTMENU) { + if (aEvent->mMessage == eContextMenu) { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (mouseEvent->context == WidgetMouseEvent::eContextMenuKey && !AdjustContextMenuKeyEvent(mouseEvent)) { diff --git a/layout/xul/nsMenuFrame.cpp b/layout/xul/nsMenuFrame.cpp index a3fd568b341..946e94846b6 100644 --- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -434,7 +434,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, (aEvent->mMessage == eMouseUp && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eRightButton) && #else - aEvent->mMessage == NS_CONTEXTMENU && + aEvent->mMessage == eContextMenu && #endif onmenu && !IsMenu() && !IsDisabled()) { // if this menu is a context menu it accepts right-clicks...fire away! diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 45a358f4b50..9a6434cf624 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -77,7 +77,7 @@ NS_EVENT_MESSAGE(ePointerLostCapture, ePointerEventFirst + 28) NS_EVENT_MESSAGE(ePointerEventLast, ePointerLostCapture) NS_EVENT_MESSAGE(eContextMenuFirst, 500) -NS_EVENT_MESSAGE(NS_CONTEXTMENU, eContextMenuFirst) +NS_EVENT_MESSAGE(eContextMenu, eContextMenuFirst) NS_EVENT_MESSAGE(NS_STREAM_EVENT_START, 1100) NS_EVENT_MESSAGE(NS_LOAD, NS_STREAM_EVENT_START) diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index 7d9ac0b3838..f9c5e6f8a0f 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -229,7 +229,7 @@ public: mFlags.mBubbles = false; mFlags.mCancelable = false; break; - case NS_CONTEXTMENU: + case eContextMenu: button = (context == eNormal) ? eRightButton : eLeftButton; break; default: @@ -240,10 +240,10 @@ public: #ifdef DEBUG virtual ~WidgetMouseEvent() { - NS_WARN_IF_FALSE(mMessage != NS_CONTEXTMENU || + NS_WARN_IF_FALSE(mMessage != eContextMenu || button == ((context == eNormal) ? eRightButton : eLeftButton), - "Wrong button set to NS_CONTEXTMENU event?"); + "Wrong button set to eContextMenu event?"); } #endif @@ -287,7 +287,7 @@ public: */ bool IsContextMenuKeyEvent() const { - return mMessage == NS_CONTEXTMENU && context == eContextMenuKey; + return mMessage == eContextMenu && context == eContextMenuKey; } /** diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 9849a3a2e2d..36f113f0a1a 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -988,7 +988,7 @@ nsWindow::OnContextmenuEvent(AndroidGeckoEvent *ae) } // Send the contextmenu event. - WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, + WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); contextMenuEvent.refPoint = RoundedToInt(pt * GetDefaultScale()) - WidgetToScreenOffset(); diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 18167a6de7f..8a39317103b 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -5091,7 +5091,7 @@ PanGestureTypeForEvent(NSEvent* aEvent) return nil; } - WidgetMouseEvent geckoEvent(true, NS_CONTEXTMENU, mGeckoChild, + WidgetMouseEvent geckoEvent(true, eContextMenu, mGeckoChild, WidgetMouseEvent::eReal); [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; geckoEvent.button = WidgetMouseEvent::eRightButton; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 7434b5b3801..c5491a6581f 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2765,7 +2765,7 @@ nsWindow::OnButtonPressEvent(GdkEventButton *aEvent) // right menu click on linux should also pop up a context menu if (domButton == WidgetMouseEvent::eRightButton && MOZ_LIKELY(!mIsDestroyed)) { - WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, + WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, WidgetMouseEvent::eReal); InitButtonEvent(contextMenuEvent, aEvent); contextMenuEvent.pressure = mLastMotionPressure; @@ -3059,7 +3059,7 @@ nsWindow::OnKeyPressEvent(GdkEventKey *aEvent) // before we dispatch a key, check if it's the context menu key. // If so, send a context menu key event instead. if (is_context_menu_key(event)) { - WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, + WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, WidgetMouseEvent::eReal, WidgetMouseEvent::eContextMenuKey); diff --git a/widget/qt/nsWindow.cpp b/widget/qt/nsWindow.cpp index 50efe4c3c5f..05a22c498f6 100644 --- a/widget/qt/nsWindow.cpp +++ b/widget/qt/nsWindow.cpp @@ -1006,7 +1006,7 @@ nsWindow::mousePressEvent(QMouseEvent* aEvent) // Right click on linux should also pop up a context menu. if (event.button == WidgetMouseEvent::eRightButton && MOZ_LIKELY(!mIsDestroyed)) { - WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, + WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, WidgetMouseEvent::eReal); InitMouseEvent(contextMenuEvent, aEvent, 1); DispatchEvent(&contextMenuEvent, status); @@ -1140,7 +1140,7 @@ nsWindow::keyPressEvent(QKeyEvent* aEvent) // Before we dispatch a key, check if it's the context menu key. // If so, send a context menu key event instead. if (IsContextMenuKeyEvent(aEvent)) { - WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, + WidgetMouseEvent contextMenuEvent(true, eContextMenu, this, WidgetMouseEvent::eReal, WidgetMouseEvent::eContextMenuKey); return DispatchEvent(&contextMenuEvent); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index aa8e56488b0..4f623508d77 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -4052,7 +4052,7 @@ nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam, WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal, aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey : WidgetMouseEvent::eNormal); - if (aEventMessage == NS_CONTEXTMENU && aIsContextMenuKey) { + if (aEventMessage == eContextMenu && aIsContextMenuKey) { nsIntPoint zero(0, 0); InitEvent(event, &zero); } else { @@ -5206,7 +5206,7 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam, pos = lParamToClient(lParam); } - result = DispatchMouseEvent(NS_CONTEXTMENU, wParam, pos, contextMenukey, + result = DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey, contextMenukey ? WidgetMouseEvent::eLeftButton : WidgetMouseEvent::eRightButton, From c4a4cb2b0df8b9eeb75ccb48e697b90006a2b87e Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Mon, 31 Aug 2015 16:22:20 +0100 Subject: [PATCH 15/85] Bug 1162418 - Try to find a suitable non-zero dimension to use when containing block's inline-size depends on an SVG element which is specified as a percentage of its container. r=jwatt --- layout/svg/nsSVGOuterSVGFrame.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index 3d08d4b6258..53ae15326ad 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -168,15 +168,33 @@ nsSVGOuterSVGFrame::GetPrefISize(nsRenderingContext *aRenderingContext) DISPLAY_PREF_WIDTH(this, result); SVGSVGElement *svg = static_cast(mContent); - nsSVGLength2 &width = svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; + WritingMode wm = GetWritingMode(); + const nsSVGLength2& isize = wm.IsVertical() + ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT] + : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; - if (width.IsPercentage()) { - // It looks like our containing block's width may depend on our width. In - // that case our behavior is undefined according to CSS 2.1 section 10.3.2, - // so return zero. + if (isize.IsPercentage()) { + // It looks like our containing block's isize may depend on our isize. In + // that case our behavior is undefined according to CSS 2.1 section 10.3.2. + // As a last resort, we'll fall back to returning zero. result = nscoord(0); + + // Returning zero may be unhelpful, however, as it leads to unexpected + // disappearance of %-sized SVGs in orthogonal contexts, where our + // containing block wants to shrink-wrap. So let's look for an ancestor + // with non-zero size in this dimension, and use that as a (somewhat + // arbitrary) result instead. + nsIFrame *parent = GetParent(); + while (parent) { + nscoord parentISize = parent->GetLogicalSize(wm).ISize(wm); + if (parentISize > 0 && parentISize != NS_UNCONSTRAINEDSIZE) { + result = parentISize; + break; + } + parent = parent->GetParent(); + } } else { - result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg)); + result = nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValue(svg)); if (result < 0) { result = nscoord(0); } From cf795007fb75c31bc25afe0b3e97586844ca44a4 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Mon, 31 Aug 2015 09:45:02 -0700 Subject: [PATCH 16/85] Bug 1199884 - Keep match roles empty in BaseTraversalRules that don't provide roles. r=yzen --- accessible/jsat/TraversalRules.jsm | 25 ++-- .../tests/mochitest/jsat/doc_traversal.html | 107 +++++++++--------- .../tests/mochitest/jsat/test_traversal.html | 3 + 3 files changed, 73 insertions(+), 62 deletions(-) diff --git a/accessible/jsat/TraversalRules.jsm b/accessible/jsat/TraversalRules.jsm index aa483e5cb5f..610693f3451 100644 --- a/accessible/jsat/TraversalRules.jsm +++ b/accessible/jsat/TraversalRules.jsm @@ -29,21 +29,23 @@ let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images') function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter) { this._explicitMatchRoles = new Set(aRoles); this._matchRoles = aRoles; - if (aRoles.indexOf(Roles.LABEL) < 0) { - this._matchRoles.push(Roles.LABEL); - } - if (aRoles.indexOf(Roles.INTERNAL_FRAME) < 0) { - // Used for traversing in to child OOP frames. - this._matchRoles.push(Roles.INTERNAL_FRAME); + if (aRoles.length) { + if (aRoles.indexOf(Roles.LABEL) < 0) { + this._matchRoles.push(Roles.LABEL); + } + if (aRoles.indexOf(Roles.INTERNAL_FRAME) < 0) { + // Used for traversing in to child OOP frames. + this._matchRoles.push(Roles.INTERNAL_FRAME); + } } this._matchFunc = aMatchFunc || function() { return Filters.MATCH; }; this.preFilter = aPreFilter || gSimplePreFilter; } BaseTraversalRule.prototype = { - getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) { - aRules.value = this._matchRoles; - return aRules.value.length; + getMatchRoles: function BaseTraversalRule_getmatchRoles(aRoles) { + aRoles.value = this._matchRoles; + return aRoles.value.length; }, match: function BaseTraversalRule_match(aAccessible) @@ -54,8 +56,9 @@ BaseTraversalRule.prototype = { Filters.MATCH | Filters.IGNORE_SUBTREE : Filters.IGNORE; } - let matchResult = this._explicitMatchRoles.has(role) ? - this._matchFunc(aAccessible) : Filters.IGNORE; + let matchResult = + (this._explicitMatchRoles.has(role) || !this._explicitMatchRoles.size) ? + this._matchFunc(aAccessible) : Filters.IGNORE; // If we are on a label that nests a checkbox/radio we should land on it. // It is a bigger touch target, and it reduces clutter. diff --git a/accessible/tests/mochitest/jsat/doc_traversal.html b/accessible/tests/mochitest/jsat/doc_traversal.html index 3e5ff2c62c4..3a01cdd1ea3 100644 --- a/accessible/tests/mochitest/jsat/doc_traversal.html +++ b/accessible/tests/mochitest/jsat/doc_traversal.html @@ -10,55 +10,58 @@ -

A small first heading

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

A larger second

-
ARIA is fun
- -
ARIA fun
-
My little togglebutton
-
ARIA fun
+
+

A small first heading

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

A larger second

+
ARIA is fun
+ +
ARIA fun
+
My little togglebutton
+
ARIA fun
+

Invisible header

Programming Language
@@ -141,8 +144,10 @@ -
Last sync:2 days ago
-
+
+
Last sync:2 days ago
+
+

This is a MathML formula diff --git a/accessible/tests/mochitest/jsat/test_traversal.html b/accessible/tests/mochitest/jsat/test_traversal.html index b95453b9f2b..2da0346ce52 100644 --- a/accessible/tests/mochitest/jsat/test_traversal.html +++ b/accessible/tests/mochitest/jsat/test_traversal.html @@ -127,6 +127,9 @@ 'switch-1', 'This is a MathML formula ', 'math-1', 'with some text after.']); + queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null, + ['header-1', 'main-1', 'footer-1']); + gQueue.invoke(); } From bee3d8a678a3b12ef561b01bb42da02b9228eefb Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Thu, 27 Aug 2015 16:35:44 -0700 Subject: [PATCH 17/85] Bug 1191486 - Generators should not implement [[Construct]] but should have a prototype; r=jorendorff --- js/src/frontend/Parser.cpp | 12 ++++++------ js/src/jsfun.h | 3 +-- js/src/tests/ecma_6/Class/methDefnGen.js | 11 ++++++++--- js/src/tests/ecma_6/Class/methodsPrototype.js | 14 ++++++++++---- js/src/tests/ecma_6/Class/newTargetGenerators.js | 4 ++++ js/src/tests/ecma_6/Generators/objects.js | 8 ++++++++ js/src/tests/ecma_6/Generators/runtime.js | 16 ++++++++++++---- 7 files changed, 49 insertions(+), 19 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 36bdaed1cc5..030b7e88ae8 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1405,9 +1405,10 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, break; case Method: MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator); - flags = (generatorKind == NotGenerator - ? JSFunction::INTERPRETED_METHOD - : JSFunction::INTERPRETED_METHOD_GENERATOR); + if (generatorKind == NotGenerator) + flags = JSFunction::INTERPRETED_METHOD; + else + flags = JSFunction::INTERPRETED_METHOD_GENERATOR; allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case ClassConstructor: @@ -1424,9 +1425,8 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; default: - flags = (generatorKind == NotGenerator - ? JSFunction::INTERPRETED_NORMAL - : JSFunction::INTERPRETED_GENERATOR); + flags = JSFunction::INTERPRETED_NORMAL; + break; } fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto, diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 5fbb086baa2..1cabe8a5716 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -81,14 +81,13 @@ class JSFunction : public js::NativeObject ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR, ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA, INTERPRETED_METHOD = INTERPRETED | METHOD_KIND, - INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND, + INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND | CONSTRUCTOR, INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR, INTERPRETED_GETTER = INTERPRETED | GETTER_KIND, INTERPRETED_SETTER = INTERPRETED | SETTER_KIND, INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR, INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND, INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR, - INTERPRETED_GENERATOR = INTERPRETED, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, STABLE_ACROSS_CLONES = IS_FUN_PROTO | CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | diff --git a/js/src/tests/ecma_6/Class/methDefnGen.js b/js/src/tests/ecma_6/Class/methDefnGen.js index cee1e2e7a8f..8afd94198a8 100644 --- a/js/src/tests/ecma_6/Class/methDefnGen.js +++ b/js/src/tests/ecma_6/Class/methDefnGen.js @@ -67,7 +67,7 @@ assertEq(next.value.hello, 2); assertEq(next.value.world, 3); // prototype property -assertEq(b.g.hasOwnProperty("prototype"), false); +assertEq(b.g.hasOwnProperty("prototype"), true); // Strict mode a = {*b(c){"use strict";yield c;}}; @@ -75,8 +75,13 @@ assertEq(a.b(1).next().value, 1); a = {*["b"](c){"use strict";return c;}}; assertEq(a.b(1).next().value, 1); -// Generators should not have [[Construct]] +// Constructing a = {*g() { yield 1; }} -assertThrowsInstanceOf(() => { new a.g }, TypeError); +it = new a.g; +next = it.next(); +assertEq(next.done, false); +assertEq(next.value, 1); +next = it.next(); +assertEq(next.done, true); reportCompare(0, 0, "ok"); diff --git a/js/src/tests/ecma_6/Class/methodsPrototype.js b/js/src/tests/ecma_6/Class/methodsPrototype.js index 121497bbf63..65d5f465d77 100644 --- a/js/src/tests/ecma_6/Class/methodsPrototype.js +++ b/js/src/tests/ecma_6/Class/methodsPrototype.js @@ -13,17 +13,23 @@ class TestClass { var test = new TestClass(); -assertEq(test.constructor.hasOwnProperty('prototype'), true); +var hasPrototype = [ + test.constructor, + test.generator, + TestClass.staticGenerator +] + +for (var fun of hasPrototype) { + assertEq(fun.hasOwnProperty('prototype'), true); +} var hasNoPrototype = [ test.method, - test.generator, - TestClass.staticGenerator, Object.getOwnPropertyDescriptor(test.__proto__, 'getter').get, Object.getOwnPropertyDescriptor(test.__proto__, 'setter').set, TestClass.staticMethod, Object.getOwnPropertyDescriptor(TestClass, 'staticGetter').get, - Object.getOwnPropertyDescriptor(TestClass, 'staticSetter').set + Object.getOwnPropertyDescriptor(TestClass, 'staticSetter').set, ] for (var fun of hasNoPrototype) { diff --git a/js/src/tests/ecma_6/Class/newTargetGenerators.js b/js/src/tests/ecma_6/Class/newTargetGenerators.js index a8dbc6ad686..da611c09ecd 100644 --- a/js/src/tests/ecma_6/Class/newTargetGenerators.js +++ b/js/src/tests/ecma_6/Class/newTargetGenerators.js @@ -10,6 +10,10 @@ const ITERATIONS = 25; for (let i = 0; i < ITERATIONS; i++) assertEq(generatorNewTarget(undefined).next().value(), undefined); +for (let i = 0; i < ITERATIONS; i++) + assertEq(new generatorNewTarget(generatorNewTarget).next().value(), + generatorNewTarget); + // also check to make sure it's useful in yield inside generators. // Plus, this code is so ugly, how could it not be a test? ;) // Thanks to anba for supplying this ludicrous expression. diff --git a/js/src/tests/ecma_6/Generators/objects.js b/js/src/tests/ecma_6/Generators/objects.js index 1ffdf48fabb..20ca01cfda3 100644 --- a/js/src/tests/ecma_6/Generators/objects.js +++ b/js/src/tests/ecma_6/Generators/objects.js @@ -15,6 +15,14 @@ function TestGeneratorObject() { assertEq(String(iter), "[object Generator]"); assertDeepEq(Object.getOwnPropertyNames(iter), []); assertNotEq(g(), iter); + + // g() is the same as new g(). + iter = new g(); + assertEq(Object.getPrototypeOf(iter), g.prototype); + assertTrue(iter instanceof g); + assertEq(String(iter), "[object Generator]"); + assertDeepEq(Object.getOwnPropertyNames(iter), []); + assertNotEq(new g(), iter); } TestGeneratorObject(); diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index 9f07ebe7862..9c5c3198caf 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -18,12 +18,11 @@ var GeneratorFunctionPrototype = Object.getPrototypeOf(g); var GeneratorFunction = GeneratorFunctionPrototype.constructor; var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; + // A generator function should have the same set of properties as any -// other function, minus a prototype. +// other function. function TestGeneratorFunctionInstance() { var f_own_property_names = Object.getOwnPropertyNames(f); - f_own_property_names.splice(f_own_property_names.indexOf("prototype"), 1); - var g_own_property_names = Object.getOwnPropertyNames(g); f_own_property_names.sort(); @@ -42,6 +41,7 @@ function TestGeneratorFunctionInstance() { } TestGeneratorFunctionInstance(); + // Generators have an additional object interposed in the chain between // themselves and Function.prototype. function TestGeneratorFunctionPrototype() { @@ -112,7 +112,15 @@ TestGeneratorFunction(); function TestPerGeneratorPrototype() { - assertEq(g.hasOwnProperty("prototype"), false); + assertNotEq((function*(){}).prototype, (function*(){}).prototype); + assertNotEq((function*(){}).prototype, g.prototype); + assertEq(typeof GeneratorFunctionPrototype, "object"); + assertEq(g.prototype.__proto__.constructor, GeneratorFunctionPrototype, "object"); + assertEq(Object.getPrototypeOf(g.prototype), GeneratorObjectPrototype); + assertFalse(g.prototype instanceof Function); + assertEq(typeof (g.prototype), "object"); + + assertDeepEq(Object.getOwnPropertyNames(g.prototype), []); } TestPerGeneratorPrototype(); From 1e1c4e5f235bd696fd0bb0844f3d89ca5e3a304b Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Mon, 31 Aug 2015 19:35:05 +0200 Subject: [PATCH 18/85] Bug 1200108 - Remove NewDenseArray intrinsic, use std_Array instead. r=till --- js/src/builtin/Array.js | 8 +++---- js/src/jit-test/tests/basic/bug1195298.js | 9 -------- js/src/jit-test/tests/basic/bug1200108.js | 5 ++++ js/src/jscntxt.h | 1 - js/src/vm/SelfHosting.cpp | 28 +---------------------- 5 files changed, 9 insertions(+), 42 deletions(-) delete mode 100644 js/src/jit-test/tests/basic/bug1195298.js create mode 100644 js/src/jit-test/tests/basic/bug1200108.js diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index aa67700fdc3..c595f20f847 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -253,7 +253,7 @@ function ArrayMap(callbackfn/*, thisArg*/) { var T = arguments.length > 1 ? arguments[1] : void 0; /* Step 6. */ - var A = NewDenseArray(len); + var A = std_Array(len); /* Step 7-8. */ /* Step a (implicit), and d. */ @@ -718,9 +718,7 @@ function ArrayIteratorNext() { } if (itemKind === ITEM_KIND_KEY_AND_VALUE) { - var pair = NewDenseArray(2); - pair[0] = index; - pair[1] = a[index]; + var pair = [index, a[index]]; result.value = pair; return result; } @@ -807,7 +805,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { var len = ToLength(arrayLike.length); // Steps 12-14. - var A = IsConstructor(C) ? new C(len) : NewDenseArray(len); + var A = IsConstructor(C) ? new C(len) : std_Array(len); // Steps 15-16. for (var k = 0; k < len; k++) { diff --git a/js/src/jit-test/tests/basic/bug1195298.js b/js/src/jit-test/tests/basic/bug1195298.js deleted file mode 100644 index fcf4a9e5bfd..00000000000 --- a/js/src/jit-test/tests/basic/bug1195298.js +++ /dev/null @@ -1,9 +0,0 @@ -function t() { - var o = {l: 0xfffffffff}; - var l = o.l - 0xffffffffe; - var a = getSelfHostedValue('NewDenseArray'); - var arr = a(l); - assertEq(arr.length, 1); -} -t(); -t(); diff --git a/js/src/jit-test/tests/basic/bug1200108.js b/js/src/jit-test/tests/basic/bug1200108.js new file mode 100644 index 00000000000..1af8826364c --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1200108.js @@ -0,0 +1,5 @@ +// |jit-test| error: 987 +var obj = {length: -1, 0: 0}; +Array.prototype.map.call(obj, function () { + throw 987; +}); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index bd5d8b0773c..21da4a6a8ae 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -754,7 +754,6 @@ bool intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 9b5168e8fff..9282130b4a7 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -287,31 +287,6 @@ intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp) return true; } -/* - * NewDenseArray(length): Allocates and returns a new dense array with - * the given length where all values are initialized to holes. - */ -bool -js::intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - double lengthDouble = args[0].toNumber(); - MOZ_ASSERT(lengthDouble >= 0); - MOZ_ASSERT(lengthDouble < INT32_MAX); - MOZ_ASSERT(uint32_t(lengthDouble) == lengthDouble); - - uint32_t length = uint32_t(lengthDouble); - - // Make a new buffer and initialize it up to length. - RootedObject buffer(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, length)); - if (!buffer) - return false; - - args.rval().setObject(*buffer); - return true; -} - bool js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) { @@ -1270,6 +1245,7 @@ intrinsic_ConstructorForTypedArray(JSContext* cx, unsigned argc, Value* vp) // Additionally, a set of C++-implemented helper functions is defined on the // self-hosting global. static const JSFunctionSpec intrinsic_functions[] = { + JS_FN("std_Array", ArrayConstructor, 1,0), JS_FN("std_Array_join", array_join, 1,0), JS_FN("std_Array_push", array_push, 1,0), JS_FN("std_Array_pop", array_pop, 0,0), @@ -1417,8 +1393,6 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0), - JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0), - // See builtin/TypedObject.h for descriptors of the typedobj functions. JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0), JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0), From f60ac5193ebec0b5ced814249d05b88382a5ec1a Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 12:50:20 -0400 Subject: [PATCH 19/85] Bug 1200208 - Send the audio-playback notification when the page calls HTMLMediaElement::Play() before the metadata has been fully loaded; r=baku --- dom/base/test/mochitest.ini | 2 + .../test_audioNotificationWithEarlyPlay.html | 73 +++++++++++++++++++ dom/html/HTMLMediaElement.cpp | 69 +++++++++++++++--- dom/html/HTMLMediaElement.h | 16 ++-- 4 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 dom/base/test/test_audioNotificationWithEarlyPlay.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 9a102c6b6f9..e8023134bc8 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,6 +267,8 @@ support-files = skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' +[test_audioNotificationWithEarlyPlay.html] +skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] diff --git a/dom/base/test/test_audioNotificationWithEarlyPlay.html b/dom/base/test/test_audioNotificationWithEarlyPlay.html new file mode 100644 index 00000000000..66184ae03ab --- /dev/null +++ b/dom/base/test/test_audioNotificationWithEarlyPlay.html @@ -0,0 +1,73 @@ + + + + Test for audio controller in windows + + + + +

+
+ + + + + diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 2e47c094648..f76e600f4b6 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3184,11 +3184,47 @@ void HTMLMediaElement::ProcessMediaFragmentURI() } } +class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent +{ + nsRefPtr mElement; + bool mShouldNotify; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; +public: + AutoNotifyAudioChannelAgent(HTMLMediaElement* aElement, + bool aNotify + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mElement(aElement) + , mShouldNotify(aNotify) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (mShouldNotify) { + mElement->NotifyAudioChannelAgent(false); + } + } + ~AutoNotifyAudioChannelAgent() + { + if (mShouldNotify) { + // The audio channel agent is destroyed at this point. + if (mElement->MaybeCreateAudioChannelAgent()) { + mElement->NotifyAudioChannelAgent(true); + } + } + } +}; + void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, nsAutoPtr aTags) { MOZ_ASSERT(NS_IsMainThread()); + // If the element is gaining or losing an audio track, we need to notify + // the audio channel agent so that the correct audio-playback events will + // get dispatched. + bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio(); + AutoNotifyAudioChannelAgent autoNotify(this, + audioTrackChanging && + mPlayingThroughTheAudioChannel); + mMediaInfo = *aInfo; mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME @@ -4488,7 +4524,25 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) return NS_OK; } -void HTMLMediaElement::UpdateAudioChannelPlayingState() +bool +HTMLMediaElement::MaybeCreateAudioChannelAgent() +{ + if (!mAudioChannelAgent) { + nsresult rv; + mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + MOZ_ASSERT(mAudioChannelAgent); + mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), + static_cast(mAudioChannel), + this); + } + return true; +} + +void +HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = (!mPaused && @@ -4506,18 +4560,9 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState() return; } - if (!mAudioChannelAgent) { - nsresult rv; - mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); - if (!mAudioChannelAgent) { - return; - } - mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), - static_cast(mAudioChannel), - this); + if (MaybeCreateAudioChannelAgent()) { + NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } - - NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 664b508dc4b..410168beaed 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,10 +35,6 @@ // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ -class nsIChannel; -class nsIHttpChannel; -class nsILoadGroup; - typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; @@ -56,9 +52,13 @@ class MediaTrack; } // namespace dom } // namespace mozilla +class AutoNotifyAudioChannelAgent; +class nsIChannel; +class nsIHttpChannel; +class nsILoadGroup; +class nsIRunnable; class nsITimer; class nsRange; -class nsIRunnable; namespace mozilla { namespace dom { @@ -78,6 +78,8 @@ class HTMLMediaElement : public nsGenericHTMLElement, public MediaDecoderOwner, public nsIAudioChannelAgentCallback { + friend class AutoNotifyAudioChannelAgent; + public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::layers::ImageContainer ImageContainer; @@ -1050,6 +1052,10 @@ protected: // Notifies the audio channel agent when the element starts or stops playing. void NotifyAudioChannelAgent(bool aPlaying); + // Creates the audio channel agent if needed. Returns true if the audio + // channel agent is ready to be used. + bool MaybeCreateAudioChannelAgent(); + class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. From e6e492afd458807c73df44be1f805ce0cd5508c2 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 31 Aug 2015 14:11:58 -0400 Subject: [PATCH 20/85] Backed out changeset 6b264cf230a0 (bug 1200208) for Windows bustage. CLOSED TREE --- dom/base/test/mochitest.ini | 2 - .../test_audioNotificationWithEarlyPlay.html | 73 ------------------- dom/html/HTMLMediaElement.cpp | 69 +++--------------- dom/html/HTMLMediaElement.h | 16 ++-- 4 files changed, 17 insertions(+), 143 deletions(-) delete mode 100644 dom/base/test/test_audioNotificationWithEarlyPlay.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index e8023134bc8..9a102c6b6f9 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,8 +267,6 @@ support-files = skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' -[test_audioNotificationWithEarlyPlay.html] -skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] diff --git a/dom/base/test/test_audioNotificationWithEarlyPlay.html b/dom/base/test/test_audioNotificationWithEarlyPlay.html deleted file mode 100644 index 66184ae03ab..00000000000 --- a/dom/base/test/test_audioNotificationWithEarlyPlay.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - Test for audio controller in windows - - - - -
-
- - - - - diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index f76e600f4b6..2e47c094648 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3184,47 +3184,11 @@ void HTMLMediaElement::ProcessMediaFragmentURI() } } -class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent -{ - nsRefPtr mElement; - bool mShouldNotify; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; -public: - AutoNotifyAudioChannelAgent(HTMLMediaElement* aElement, - bool aNotify - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : mElement(aElement) - , mShouldNotify(aNotify) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (mShouldNotify) { - mElement->NotifyAudioChannelAgent(false); - } - } - ~AutoNotifyAudioChannelAgent() - { - if (mShouldNotify) { - // The audio channel agent is destroyed at this point. - if (mElement->MaybeCreateAudioChannelAgent()) { - mElement->NotifyAudioChannelAgent(true); - } - } - } -}; - void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, nsAutoPtr aTags) { MOZ_ASSERT(NS_IsMainThread()); - // If the element is gaining or losing an audio track, we need to notify - // the audio channel agent so that the correct audio-playback events will - // get dispatched. - bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio(); - AutoNotifyAudioChannelAgent autoNotify(this, - audioTrackChanging && - mPlayingThroughTheAudioChannel); - mMediaInfo = *aInfo; mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME @@ -4524,25 +4488,7 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) return NS_OK; } -bool -HTMLMediaElement::MaybeCreateAudioChannelAgent() -{ - if (!mAudioChannelAgent) { - nsresult rv; - mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - MOZ_ASSERT(mAudioChannelAgent); - mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), - static_cast(mAudioChannel), - this); - } - return true; -} - -void -HTMLMediaElement::UpdateAudioChannelPlayingState() +void HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = (!mPaused && @@ -4560,9 +4506,18 @@ HTMLMediaElement::UpdateAudioChannelPlayingState() return; } - if (MaybeCreateAudioChannelAgent()) { - NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); + if (!mAudioChannelAgent) { + nsresult rv; + mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); + if (!mAudioChannelAgent) { + return; + } + mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), + static_cast(mAudioChannel), + this); } + + NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 410168beaed..664b508dc4b 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,6 +35,10 @@ // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ +class nsIChannel; +class nsIHttpChannel; +class nsILoadGroup; + typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; @@ -52,13 +56,9 @@ class MediaTrack; } // namespace dom } // namespace mozilla -class AutoNotifyAudioChannelAgent; -class nsIChannel; -class nsIHttpChannel; -class nsILoadGroup; -class nsIRunnable; class nsITimer; class nsRange; +class nsIRunnable; namespace mozilla { namespace dom { @@ -78,8 +78,6 @@ class HTMLMediaElement : public nsGenericHTMLElement, public MediaDecoderOwner, public nsIAudioChannelAgentCallback { - friend class AutoNotifyAudioChannelAgent; - public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::layers::ImageContainer ImageContainer; @@ -1052,10 +1050,6 @@ protected: // Notifies the audio channel agent when the element starts or stops playing. void NotifyAudioChannelAgent(bool aPlaying); - // Creates the audio channel agent if needed. Returns true if the audio - // channel agent is ready to be used. - bool MaybeCreateAudioChannelAgent(); - class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. From 303f86160f45574cb45a687f348e8b86673b3ab5 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Fri, 28 Aug 2015 16:43:54 -0400 Subject: [PATCH 21/85] Bug 1194224 - adding support for Shadow DOM in marionette. r=automatedtester --- testing/marionette/actions.js | 30 +- .../marionette/tests/unit/test_shadow_dom.py | 65 +++++ .../marionette/tests/unit/unit-tests.ini | 2 + .../marionette/www/test_shadow_dom.html | 26 ++ testing/marionette/driver.js | 47 ++-- .../driver/marionette_driver/marionette.py | 15 + testing/marionette/elements.js | 76 +++-- testing/marionette/listener.js | 261 ++++++++++-------- 8 files changed, 356 insertions(+), 166 deletions(-) create mode 100644 testing/marionette/client/marionette/tests/unit/test_shadow_dom.py create mode 100644 testing/marionette/client/marionette/www/test_shadow_dom.html diff --git a/testing/marionette/actions.js b/testing/marionette/actions.js index a284909ecf2..0d534315507 100644 --- a/testing/marionette/actions.js +++ b/testing/marionette/actions.js @@ -40,7 +40,7 @@ this.ActionChain = function(utils, checkForInterrupted) { ActionChain.prototype.dispatchActions = function( args, touchId, - frame, + container, elementManager, callbacks, touchProvider) { @@ -51,16 +51,16 @@ ActionChain.prototype.dispatchActions = function( } this.elementManager = elementManager; - let commandArray = elementManager.convertWrappedArguments(args, frame); + let commandArray = elementManager.convertWrappedArguments(args, container); this.onSuccess = callbacks.onSuccess; this.onError = callbacks.onError; - this.frame = frame; + this.container = container; if (touchId == null) { touchId = this.nextTouchId++; } - if (!frame.document.createTouch) { + if (!container.frame.document.createTouch) { this.mouseEventsOnly = true; } @@ -126,7 +126,7 @@ ActionChain.prototype.emitMouseEvent = function( type, elClientX, elClientY, - button || 0, + button || 0, clickCount || 1, mods, false, @@ -141,7 +141,7 @@ ActionChain.prototype.emitMouseEvent = function( ActionChain.prototype.resetValues = function() { this.onSuccess = null; this.onError = null; - this.frame = null; + this.container = null; this.elementManager = null; this.touchProvider = null; this.mouseEventsOnly = false; @@ -177,17 +177,17 @@ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { switch(command) { case "keyDown": - this.utils.sendKeyDown(pack[1], keyModifiers, this.frame); + this.utils.sendKeyDown(pack[1], keyModifiers, this.container.frame); this.actions(chain, touchId, i, keyModifiers); break; case "keyUp": - this.utils.sendKeyUp(pack[1], keyModifiers, this.frame); + this.utils.sendKeyUp(pack[1], keyModifiers, this.container.frame); this.actions(chain, touchId, i, keyModifiers); break; case "click": - el = this.elementManager.getKnownElement(pack[1], this.frame); + el = this.elementManager.getKnownElement(pack[1], this.container); let button = pack[2]; let clickCount = pack[3]; c = this.coordinates(el, null, null); @@ -217,7 +217,7 @@ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) { this.scrolling = true; } - el = this.elementManager.getKnownElement(pack[1], this.frame); + el = this.elementManager.getKnownElement(pack[1], this.container); c = this.coordinates(el, pack[2], pack[3]); touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers); this.actions(chain, touchId, i, keyModifiers); @@ -236,7 +236,7 @@ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { break; case "move": - el = this.elementManager.getKnownElement(pack[1], this.frame); + el = this.elementManager.getKnownElement(pack[1], this.container); c = this.coordinates(el); this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers); this.actions(chain, touchId, i, keyModifiers); @@ -274,7 +274,7 @@ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { this.actions(chain, touchId, i, keyModifiers); } break; - + case "cancel": this.generateEvents( "cancel", @@ -286,7 +286,7 @@ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { this.actions(chain, touchId, i, keyModifiers); this.scrolling = false; break; - + case "longPress": this.generateEvents( "contextmenu", @@ -355,7 +355,7 @@ ActionChain.prototype.getCoordinateInfo = function(el, corx, cory) { ActionChain.prototype.generateEvents = function( type, x, y, touchId, target, keyModifiers) { this.lastCoordinates = [x, y]; - let doc = this.frame.document; + let doc = this.container.frame.document; switch (type) { case "tap": @@ -450,7 +450,7 @@ ActionChain.prototype.generateEvents = function( case "contextmenu": this.isTap = false; - let event = this.frame.document.createEvent("MouseEvents"); + let event = this.container.frame.document.createEvent("MouseEvents"); if (this.mouseEventsOnly) { target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]); } else { diff --git a/testing/marionette/client/marionette/tests/unit/test_shadow_dom.py b/testing/marionette/client/marionette/tests/unit/test_shadow_dom.py new file mode 100644 index 00000000000..4c98eb38228 --- /dev/null +++ b/testing/marionette/client/marionette/tests/unit/test_shadow_dom.py @@ -0,0 +1,65 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from marionette import MarionetteTestCase +from marionette_driver.errors import (NoSuchElementException, StaleElementException) + + +class TestShadowDom(MarionetteTestCase): + + def setUp(self): + MarionetteTestCase.setUp(self) + self.marionette.enforce_gecko_prefs({"dom.webcomponents.enabled": True}) + self.marionette.navigate(self.marionette.absolute_url("test_shadow_dom.html")) + + self.host = self.marionette.find_element("id", "host") + self.marionette.switch_to_shadow_root(self.host) + self.button = self.marionette.find_element("id", "button") + + def test_shadow_dom(self): + # Button in shadow root should be actionable + self.button.click() + + def test_shadow_dom_after_switch_away_from_shadow_root(self): + # Button in shadow root should be actionable + self.button.click() + self.marionette.switch_to_shadow_root() + # After switching back to top content, button should be stale + self.assertRaises(StaleElementException, self.button.click) + + def test_shadow_dom_raises_stale_element_exception_when_button_remove(self): + self.marionette.execute_script( + 'document.getElementById("host").shadowRoot.getElementById("button").remove();') + # After removing button from shadow DOM, button should be stale + self.assertRaises(StaleElementException, self.button.click) + + def test_shadow_dom_raises_stale_element_exception_when_host_removed(self): + self.marionette.execute_script('document.getElementById("host").remove();') + # After removing shadow DOM host element, button should be stale + self.assertRaises(StaleElementException, self.button.click) + + def test_non_existent_shadow_dom(self): + # Jump back to top level content + self.marionette.switch_to_shadow_root() + # When no ShadowRoot is found, switch_to_shadow_root throws NoSuchElementException + self.assertRaises(NoSuchElementException, self.marionette.switch_to_shadow_root, + self.marionette.find_element("id", "empty-host")) + + def test_inner_shadow_dom(self): + # Button in shadow root should be actionable + self.button.click() + self.inner_host = self.marionette.find_element("id", "inner-host") + self.marionette.switch_to_shadow_root(self.inner_host) + self.inner_button = self.marionette.find_element("id", "inner-button") + # Nested nutton in nested shadow root should be actionable + self.inner_button.click() + self.marionette.switch_to_shadow_root() + # After jumping back to parent shadow root, button should again be actionable but inner + # button should now be stale + self.button.click() + self.assertRaises(StaleElementException, self.inner_button.click) + self.marionette.switch_to_shadow_root() + # After switching back to top content, both buttons should now be stale + self.assertRaises(StaleElementException, self.button.click) + self.assertRaises(StaleElementException, self.inner_button.click) diff --git a/testing/marionette/client/marionette/tests/unit/unit-tests.ini b/testing/marionette/client/marionette/tests/unit/unit-tests.ini index c21e4725e00..9b738443578 100644 --- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini @@ -158,3 +158,5 @@ skip-if = os == "win" # http://bugs.python.org/issue14574 [test_execute_sandboxes.py] [test_using_permissions.py] + +[test_shadow_dom.py] diff --git a/testing/marionette/client/marionette/www/test_shadow_dom.html b/testing/marionette/client/marionette/www/test_shadow_dom.html new file mode 100644 index 00000000000..3ee893e6d58 --- /dev/null +++ b/testing/marionette/client/marionette/www/test_shadow_dom.html @@ -0,0 +1,26 @@ + + + + + + + +Marionette Test + + +
+
+ + + diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 45550ff67b6..2e713165151 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -791,7 +791,8 @@ GeckoDriver.prototype.createExecuteSandbox = function(win, mn, sandboxName) { * execution sandbox. */ GeckoDriver.prototype.applyArgumentsToSandbox = function(win, sb, args) { - sb.__marionetteParams = this.curBrowser.elementManager.convertWrappedArguments(args, win); + sb.__marionetteParams = this.curBrowser.elementManager.convertWrappedArguments(args, + { frame: win }); sb.__namedArgs = this.curBrowser.elementManager.applyNamedArgs(args); }; @@ -1656,7 +1657,7 @@ GeckoDriver.prototype.switchToFrame = function(cmd, resp) { if (this.curBrowser.elementManager.seenItems[cmd.parameters.element]) { // HTMLIFrameElement let wantedFrame = this.curBrowser.elementManager - .getKnownElement(cmd.parameters.element, curWindow); + .getKnownElement(cmd.parameters.element, { frame: curWindow }); // Deal with an embedded xul:browser case if (wantedFrame.tagName == "xul:browser" || wantedFrame.tagName == "browser") { curWindow = wantedFrame.contentWindow; @@ -1837,7 +1838,7 @@ GeckoDriver.prototype.actionChain = function(cmd, resp) { let win = this.getCurrentWindow(); let elm = this.curBrowser.elementManager; - this.actions.dispatchActions(chain, nextId, win, elm, cbs); + this.actions.dispatchActions(chain, nextId, { frame: win }, elm, cbs); break; case Context.CONTENT: @@ -1882,7 +1883,7 @@ GeckoDriver.prototype.findElement = function(cmd, resp) { resp.value = yield new Promise((resolve, reject) => { let win = this.getCurrentWindow(); this.curBrowser.elementManager.find( - win, + { frame: win }, cmd.parameters, this.searchTimeout, false /* all */, @@ -1934,7 +1935,7 @@ GeckoDriver.prototype.findElements = function(cmd, resp) { resp.value = yield new Promise((resolve, reject) => { let win = this.getCurrentWindow(); this.curBrowser.elementManager.find( - win, + { frame: win }, cmd.parameters, this.searchTimeout, true /* all */, @@ -1990,7 +1991,7 @@ GeckoDriver.prototype.clickElement = function(cmd, resp) { case Context.CHROME: // click atom fails, fall back to click() action let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); el.click(); break; @@ -2019,7 +2020,7 @@ GeckoDriver.prototype.getElementAttribute = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); resp.value = utils.getElementAttribute(el, name); break; @@ -2043,7 +2044,7 @@ GeckoDriver.prototype.getElementText = function(cmd, resp) { case Context.CHROME: // for chrome, we look at text nodes, and any node with a "label" field let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); let lines = []; this.getVisibleText(el, lines); resp.value = lines.join("\n"); @@ -2067,7 +2068,7 @@ GeckoDriver.prototype.getElementTagName = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); resp.value = el.tagName.toLowerCase(); break; @@ -2089,7 +2090,7 @@ GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); resp.value = utils.isElementDisplayed(el); break; @@ -2113,7 +2114,7 @@ GeckoDriver.prototype.getElementValueOfCssProperty = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); let sty = win.document.defaultView.getComputedStyle(el, null); resp.value = sty.getPropertyValue(prop); break; @@ -2137,7 +2138,7 @@ GeckoDriver.prototype.isElementEnabled = function(cmd, resp) { case Context.CHROME: // Selenium atom doesn't quite work here let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); resp.value = !(!!el.disabled); break; @@ -2160,7 +2161,7 @@ GeckoDriver.prototype.isElementSelected = function(cmd, resp) { case Context.CHROME: // Selenium atom doesn't quite work here let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); if (typeof el.checked != "undefined") { resp.value = !!el.checked; } else if (typeof el.selected != "undefined") { @@ -2182,7 +2183,7 @@ GeckoDriver.prototype.getElementSize = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); let rect = el.getBoundingClientRect(); resp.value = {width: rect.width, height: rect.height}; break; @@ -2199,7 +2200,7 @@ GeckoDriver.prototype.getElementRect = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); let rect = el.getBoundingClientRect(); resp.value = { x: rect.x + win.pageXOffset, @@ -2233,7 +2234,7 @@ GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); utils.sendKeysToElement( win, el, @@ -2299,7 +2300,7 @@ GeckoDriver.prototype.clearElement = function(cmd, resp) { case Context.CHROME: // the selenium atom doesn't work here let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, win); + let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); if (el.nodeName == "textbox") { el.value = ""; } else if (el.nodeName == "checkbox") { @@ -2327,6 +2328,17 @@ GeckoDriver.prototype.getElementLocation = function(cmd, resp) { return this.listener.getElementLocation(cmd.parameters.id); }; +/** + * Switch to shadow root of the given host element. + * + * @param {string} id element id. + */ +GeckoDriver.prototype.switchToShadowRoot = function(cmd, resp) { + let id; + if (cmd.parameters) { id = cmd.parameters.id; } + yield this.listener.switchToShadowRoot(id); +}; + /** Add a cookie to the document. */ GeckoDriver.prototype.addCookie = function(cmd, resp) { yield this.listener.addCookie({cookie: cmd.parameters.cookie}); @@ -3019,6 +3031,7 @@ GeckoDriver.prototype.commands = { "getActiveFrame": GeckoDriver.prototype.getActiveFrame, "switchToFrame": GeckoDriver.prototype.switchToFrame, "switchToWindow": GeckoDriver.prototype.switchToWindow, + "switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot, "deleteSession": GeckoDriver.prototype.deleteSession, "importScript": GeckoDriver.prototype.importScript, "clearImportedScripts": GeckoDriver.prototype.clearImportedScripts, diff --git a/testing/marionette/driver/marionette_driver/marionette.py b/testing/marionette/driver/marionette_driver/marionette.py index 56b156395a1..d3131ca2c1f 100644 --- a/testing/marionette/driver/marionette_driver/marionette.py +++ b/testing/marionette/driver/marionette_driver/marionette.py @@ -1271,6 +1271,21 @@ class Marionette(object): body["id"] = frame self._send_message("switchToFrame", body) + def switch_to_shadow_root(self, host=None): + """Switch the current context to the specified host's Shadow DOM. + Subsequent commands will operate in the context of the specified Shadow + DOM, if applicable. + + :param host: A reference to the host element containing Shadow DOM. + This can be an ``HTMLElement``. If you call + ``switch_to_shadow_root`` without an argument, it will switch to the + parent Shadow DOM or the top-level frame. + """ + body = {} + if isinstance(host, HTMLElement): + body["id"] = host.id + return self._send_message("switchToShadowRoot", body) + def get_url(self): """Get a string representing the current URL. diff --git a/testing/marionette/elements.js b/testing/marionette/elements.js index 35b7ece6357..bc35cbbe2a6 100644 --- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -244,13 +244,13 @@ ElementManager.prototype = { * * @param String id * The DOM reference ID - * @param nsIDOMWindow win - * The window that contains the element + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element * * @returns nsIDOMElement * Returns the element or throws Exception if not found */ - getKnownElement: function EM_getKnownElement(id, win) { + getKnownElement: function EM_getKnownElement(id, container) { let el = this.seenItems[id]; if (!el) { throw new JavaScriptError("Element has not been seen before. Id given was " + id); @@ -263,11 +263,16 @@ ElementManager.prototype = { delete this.seenItems[id]; } // use XPCNativeWrapper to compare elements; see bug 834266 - let wrappedWin = XPCNativeWrapper(win); + let wrappedFrame = XPCNativeWrapper(container.frame); + let wrappedShadowRoot; + if (container.shadowRoot) { + wrappedShadowRoot = XPCNativeWrapper(container.shadowRoot); + } + if (!el || - !(XPCNativeWrapper(el).ownerDocument == wrappedWin.document) || - (XPCNativeWrapper(el).compareDocumentPosition(wrappedWin.document.documentElement) & - DOCUMENT_POSITION_DISCONNECTED)) { + !(XPCNativeWrapper(el).ownerDocument == wrappedFrame.document) || + this.isDisconnected(XPCNativeWrapper(el), wrappedShadowRoot, + wrappedFrame)) { throw new StaleElementReferenceError( "The element reference is stale. Either the element " + "is no longer attached to the DOM or the page has been refreshed."); @@ -275,6 +280,36 @@ ElementManager.prototype = { return el; }, + /** + * Check if the element is detached from the current frame as well as the + * optional shadow root (when inside a Shadow DOM context). + * @param nsIDOMElement el + * element to be checked + * @param ShadowRoot shadowRoot + * an optional shadow root containing an element + * @param nsIDOMWindow frame + * window that contains the element or the current host of the shadow + * root. + * @return {Boolean} a flag indicating that the element is disconnected + */ + isDisconnected: function EM_isDisconnected(el, shadowRoot, frame) { + if (shadowRoot && frame.ShadowRoot) { + if (el.compareDocumentPosition(shadowRoot) & + DOCUMENT_POSITION_DISCONNECTED) { + return true; + } + // Looking for next possible ShadowRoot ancestor + let parent = shadowRoot.host; + while (parent && !(parent instanceof frame.ShadowRoot)) { + parent = parent.parentNode; + } + return this.isDisconnected(shadowRoot.host, parent, frame); + } else { + return el.compareDocumentPosition(frame.document.documentElement) & + DOCUMENT_POSITION_DISCONNECTED; + } + }, + /** * Convert values to primitives that can be transported over the * Marionette protocol. @@ -338,14 +373,14 @@ ElementManager.prototype = { * * @param object args * Arguments passed in by client - * @param nsIDOMWindow win - * The window that contains the elements + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element * * @returns object * Returns the objects passed in by the client, with the * reference IDs replaced by the actual elements. */ - convertWrappedArguments: function EM_convertWrappedArguments(args, win) { + convertWrappedArguments: function EM_convertWrappedArguments(args, container) { let converted; switch (typeof(args)) { case 'number': @@ -360,14 +395,14 @@ ElementManager.prototype = { else if (Object.prototype.toString.call(args) == '[object Array]') { converted = []; for (let i in args) { - converted.push(this.convertWrappedArguments(args[i], win)); + converted.push(this.convertWrappedArguments(args[i], container)); } } else if (((typeof(args[this.elementKey]) === 'string') && args.hasOwnProperty(this.elementKey)) || ((typeof(args[this.w3cElementKey]) === 'string') && args.hasOwnProperty(this.w3cElementKey))) { let elementUniqueIdentifier = args[this.w3cElementKey] ? args[this.w3cElementKey] : args[this.elementKey]; - converted = this.getKnownElement(elementUniqueIdentifier, win); + converted = this.getKnownElement(elementUniqueIdentifier, container); if (converted == null) { throw new WebDriverError(`Unknown element: ${elementUniqueIdentifier}`); } @@ -375,7 +410,7 @@ ElementManager.prototype = { else { converted = {}; for (let prop in args) { - converted[prop] = this.convertWrappedArguments(args[prop], win); + converted[prop] = this.convertWrappedArguments(args[prop], container); } } break; @@ -417,8 +452,8 @@ ElementManager.prototype = { * given node, using the given search strategy. Search * will continue until the search timelimit has been reached. * - * @param nsIDOMWindow win - * The window to search in + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element * @param object values * The 'using' member of values will tell us which search * method to use. The 'value' member tells us the value we @@ -438,15 +473,16 @@ ElementManager.prototype = { * @return nsIDOMElement or list of nsIDOMElements * Returns the element(s) by calling the on_success function. */ - find: function EM_find(win, values, searchTimeout, all, on_success, on_error, command_id) { + find: function EM_find(container, values, searchTimeout, all, on_success, on_error, command_id) { let startTime = values.time ? values.time : new Date().getTime(); + let rootNode = container.shadowRoot || container.frame.document; let startNode = (values.element != undefined) ? - this.getKnownElement(values.element, win) : win.document; + this.getKnownElement(values.element, container) : rootNode; if (this.elementStrategies.indexOf(values.using) < 0) { throw new InvalidSelectorError("No such strategy: " + values.using); } - let found = all ? this.findElements(values.using, values.value, win.document, startNode) : - this.findElement(values.using, values.value, win.document, startNode); + let found = all ? this.findElements(values.using, values.value, rootNode, startNode) : + this.findElement(values.using, values.value, rootNode, startNode); let type = Object.prototype.toString.call(found); let isArrayLike = ((type == '[object Array]') || (type == '[object HTMLCollection]') || (type == '[object NodeList]')); if (found == null || (isArrayLike && found.length <= 0)) { @@ -465,7 +501,7 @@ ElementManager.prototype = { } } else { values.time = startTime; - this.timer.initWithCallback(this.find.bind(this, win, values, + this.timer.initWithCallback(this.find.bind(this, container, values, searchTimeout, all, on_success, on_error, command_id), diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index 10a87239480..5f0830e8c92 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -35,9 +35,9 @@ let marionetteTestName; let winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let listenerId = null; // unique ID of this listener -let curFrame = content; -let isRemoteBrowser = () => curFrame.contentWindow !== null; -let previousFrame = null; +let curContainer = { frame: content, shadowRoot: null }; +let isRemoteBrowser = () => curContainer.frame.contentWindow !== null; +let previousContainer = null; let elementManager = new ElementManager([]); let accessibility = new Accessibility(); let actions = new ActionChain(utils, checkForInterrupted); @@ -84,9 +84,9 @@ let modalHandler = function() { sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true }); let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value; if (isLocal) { - previousFrame = curFrame; + previousContainer = curContainer; } - curFrame = content; + curContainer = { frame: content, shadowRoot: null }; }; /** @@ -123,7 +123,7 @@ function emitTouchEventForIFrame(message) { message = message.json; let identifier = actions.nextTouchId; - let domWindowUtils = curFrame. + let domWindowUtils = curContainer.frame. QueryInterface(Components.interfaces.nsIInterfaceRequestor). getInterface(Components.interfaces.nsIDOMWindowUtils); var ratio = domWindowUtils.screenPixelsPerCSSPixel; @@ -212,6 +212,7 @@ let getElementLocationFn = dispatch(getElementLocation); let clearElementFn = dispatch(clearElement); let isElementDisplayedFn = dispatch(isElementDisplayed); let getElementValueOfCssPropertyFn = dispatch(getElementValueOfCssProperty); +let switchToShadowRootFn = dispatch(switchToShadowRoot); /** * Start all message listeners @@ -251,6 +252,7 @@ function startListeners() { addMessageListenerId("Marionette:getElementLocation", getElementLocationFn); //deprecated addMessageListenerId("Marionette:clearElement", clearElementFn); addMessageListenerId("Marionette:switchToFrame", switchToFrame); + addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); addMessageListenerId("Marionette:deleteSession", deleteSession); addMessageListenerId("Marionette:sleepSession", sleepSession); addMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); @@ -356,6 +358,7 @@ function deleteSession(msg) { removeMessageListenerId("Marionette:getElementLocation", getElementLocationFn); removeMessageListenerId("Marionette:clearElement", clearElementFn); removeMessageListenerId("Marionette:switchToFrame", switchToFrame); + removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); removeMessageListenerId("Marionette:deleteSession", deleteSession); removeMessageListenerId("Marionette:sleepSession", sleepSession); removeMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); @@ -371,9 +374,9 @@ function deleteSession(msg) { content.removeEventListener("mozbrowsershowmodalprompt", modalHandler, false); } elementManager.reset(); - // reset frame to the top-most frame - curFrame = content; - curFrame.focus(); + // reset container frame to the top-most frame + curContainer = { frame: content, shadowRoot: null }; + curContainer.frame.focus(); actions.touchIds = {}; } @@ -427,7 +430,7 @@ function sendError(err, cmdId) { */ function resetValues() { sandboxes = {}; - curFrame = content; + curContainer = { frame: content, shadowRoot: null }; actions.mouseEventsOnly = false; } @@ -442,7 +445,7 @@ function dumpLog(logline) { * Check if our context was interrupted */ function wasInterrupted() { - if (previousFrame) { + if (previousContainer) { let element = content.document.elementFromPoint((content.innerWidth/2), (content.innerHeight/2)); if (element.id.indexOf("modal-dialog") == -1) { return true; @@ -456,10 +459,10 @@ function wasInterrupted() { function checkForInterrupted() { if (wasInterrupted()) { - if (previousFrame) { - //if previousFrame is set, then we're in a single process environment - curFrame = actions.frame = previousFrame; - previousFrame = null; + if (previousContainer) { + // if previousContainer is set, then we're in a single process environment + curContainer = actions.container = previousContainer; + previousContainer = null; } else { //else we're in OOP environment, so we'll switch to the original OOP frame @@ -512,11 +515,11 @@ function createExecuteContentSandbox(win, timeout) { sandbox.asyncComplete = (obj, id) => { if (id == asyncTestCommandId) { - curFrame.removeEventListener("unload", onunload, false); - curFrame.clearTimeout(asyncTestTimeoutId); + curContainer.frame.removeEventListener("unload", onunload, false); + curContainer.frame.clearTimeout(asyncTestTimeoutId); if (inactivityTimeoutId != null) { - curFrame.clearTimeout(inactivityTimeoutId); + curContainer.frame.clearTimeout(inactivityTimeoutId); } sendSyncMessage("Marionette:shareData", @@ -562,14 +565,14 @@ function executeScript(msg, directInject) { // Set up inactivity timeout. if (msg.json.inactivityTimeout) { let setTimer = function() { - inactivityTimeoutId = curFrame.setTimeout(function() { + inactivityTimeoutId = curContainer.frame.setTimeout(function() { sendError(new ScriptTimeoutError("timed out due to inactivity"), asyncTestCommandId); }, msg.json.inactivityTimeout); }; setTimer(); heartbeatCallback = function() { - curFrame.clearTimeout(inactivityTimeoutId); + curContainer.frame.clearTimeout(inactivityTimeoutId); setTimer(); }; } @@ -580,8 +583,8 @@ function executeScript(msg, directInject) { if (msg.json.newSandbox || !(sandboxName in sandboxes) || - (sandboxes[sandboxName].window != curFrame)) { - createExecuteContentSandbox(curFrame, msg.json.timeout); + (sandboxes[sandboxName].window != curContainer.frame)) { + createExecuteContentSandbox(curContainer.frame, msg.json.timeout); if (!sandboxes[sandboxName]) { sendError(new WebDriverError("Could not create sandbox!"), asyncTestCommandId); return; @@ -617,7 +620,7 @@ function executeScript(msg, directInject) { else { try { sandbox.__marionetteParams = Cu.cloneInto(elementManager.convertWrappedArguments( - msg.json.args, curFrame), sandbox, { wrapReflectors: true }); + msg.json.args, curContainer), sandbox, { wrapReflectors: true }); } catch (e) { sendError(e, asyncTestCommandId); return; @@ -711,14 +714,14 @@ function executeWithCallback(msg, useFinish) { // Set up inactivity timeout. if (msg.json.inactivityTimeout) { let setTimer = function() { - inactivityTimeoutId = curFrame.setTimeout(function() { + inactivityTimeoutId = curContainer.frame.setTimeout(function() { sandbox.asyncComplete(new ScriptTimeoutError("timed out due to inactivity"), asyncTestCommandId); }, msg.json.inactivityTimeout); }; setTimer(); heartbeatCallback = function() { - curFrame.clearTimeout(inactivityTimeoutId); + curContainer.frame.clearTimeout(inactivityTimeoutId); setTimer(); }; } @@ -730,12 +733,12 @@ function executeWithCallback(msg, useFinish) { onunload = function() { sendError(new JavaScriptError("unload was called"), asyncTestCommandId); }; - curFrame.addEventListener("unload", onunload, false); + curContainer.frame.addEventListener("unload", onunload, false); if (msg.json.newSandbox || !(sandboxName in sandboxes) || - (sandboxes[sandboxName].window != curFrame)) { - createExecuteContentSandbox(curFrame, msg.json.timeout); + (sandboxes[sandboxName].window != curContainer.frame)) { + createExecuteContentSandbox(curContainer.frame, msg.json.timeout); if (!sandboxes[sandboxName]) { sendError(new JavaScriptError("Could not create sandbox!"), asyncTestCommandId); return; @@ -747,14 +750,14 @@ function executeWithCallback(msg, useFinish) { let sandbox = sandboxes[sandboxName]; sandbox.tag = script; - asyncTestTimeoutId = curFrame.setTimeout(function() { + asyncTestTimeoutId = curContainer.frame.setTimeout(function() { sandbox.asyncComplete(new ScriptTimeoutError("timed out"), asyncTestCommandId); }, msg.json.timeout); - originalOnError = curFrame.onerror; - curFrame.onerror = function errHandler(msg, url, line) { + originalOnError = curContainer.frame.onerror; + curContainer.frame.onerror = function errHandler(msg, url, line) { sandbox.asyncComplete(new JavaScriptError(msg + "@" + url + ", line " + line), asyncTestCommandId); - curFrame.onerror = originalOnError; + curContainer.frame.onerror = originalOnError; }; let scriptSrc; @@ -767,7 +770,7 @@ function executeWithCallback(msg, useFinish) { else { try { sandbox.__marionetteParams = Cu.cloneInto(elementManager.convertWrappedArguments( - msg.json.args, curFrame), sandbox, { wrapReflectors: true }); + msg.json.args, curContainer), sandbox, { wrapReflectors: true }); } catch (e) { sendError(e, asyncTestCommandId); return; @@ -807,7 +810,7 @@ function emitTouchEvent(type, touch) { if (!wasInterrupted()) { let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport"; dumpLog(loggingInfo); - var docShell = curFrame.document.defaultView. + var docShell = curContainer.frame.document.defaultView. QueryInterface(Components.interfaces.nsIInterfaceRequestor). getInterface(Components.interfaces.nsIWebNavigation). QueryInterface(Components.interfaces.nsIDocShell); @@ -833,7 +836,7 @@ function emitTouchEvent(type, touch) { {log: elementManager.wrapValue(marionetteLogObj.getLogs())}); marionetteLogObj.clearLogs(); */ - let domWindowUtils = curFrame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); + let domWindowUtils = curContainer.frame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.clientX], [touch.clientY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0); } } @@ -866,6 +869,7 @@ function coordinates(target, x, y) { */ function elementInViewport(el, x, y) { let c = coordinates(el, x, y); + let curFrame = curContainer.frame; let viewPort = {top: curFrame.pageYOffset, left: curFrame.pageXOffset, bottom: (curFrame.pageYOffset + curFrame.innerHeight), @@ -917,7 +921,7 @@ function checkVisible(el, x, y) { function singleTap(msg) { let command_id = msg.json.command_id; try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); + let el = elementManager.getKnownElement(msg.json.id, curContainer); let acc = accessibility.getAccessibleObject(el, true); // after this block, the element will be scrolled into view let visible = checkVisible(el, msg.json.corx, msg.json.cory); @@ -927,7 +931,7 @@ function singleTap(msg) { return; } checkActionableAccessibility(acc); - if (!curFrame.document.createTouch) { + if (!curContainer.frame.document.createTouch) { actions.mouseEventsOnly = true; } let c = coordinates(el, msg.json.corx, msg.json.cory); @@ -957,7 +961,7 @@ function checkEnabledAccessibility(accesible, element, enabled) { } let disabledAccessibility = accessibility.matchState( accesible, 'STATE_UNAVAILABLE'); - let explorable = curFrame.document.defaultView.getComputedStyle( + let explorable = curContainer.frame.document.defaultView.getComputedStyle( element, null).getPropertyValue('pointer-events') !== 'none'; let message; @@ -1076,7 +1080,7 @@ function actionChain(msg) { actions.dispatchActions( args, touchId, - curFrame, + curContainer, elementManager, callbacks, touchProvider); @@ -1157,7 +1161,7 @@ function setDispatch(batches, touches, command_id, batchIndex) { command = pack[1]; switch (command) { case 'press': - el = elementManager.getKnownElement(pack[2], curFrame); + el = elementManager.getKnownElement(pack[2], curContainer); c = coordinates(el, pack[3], pack[4]); touch = createATouch(el, c.x, c.y, touchId); multiLast[touchId] = touch; @@ -1172,7 +1176,7 @@ function setDispatch(batches, touches, command_id, batchIndex) { emitMultiEvents('touchend', touch, touches); break; case 'move': - el = elementManager.getKnownElement(pack[2], curFrame); + el = elementManager.getKnownElement(pack[2], curContainer); c = coordinates(el); touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId); touchIndex = touches.indexOf(lastTouch); @@ -1226,7 +1230,7 @@ function multiAction(msg) { let maxlen = msg.json.maxlen; try { // unwrap the original nested array - let commandArray = elementManager.convertWrappedArguments(args, curFrame); + let commandArray = elementManager.convertWrappedArguments(args, curContainer); let concurrentEvent = []; let temp; for (let i = 0; i < maxlen; i++) { @@ -1270,18 +1274,19 @@ function pollForReadyState(msg, start, callback) { end = new Date().getTime(); let aboutErrorRegex = /about:.+(error)\?/; let elapse = end - start; + let doc = curContainer.frame.document; if (pageTimeout == null || elapse <= pageTimeout) { - if (curFrame.document.readyState == "complete") { + if (doc.readyState == "complete") { callback(); sendOk(command_id); - } else if (curFrame.document.readyState == "interactive" && - aboutErrorRegex.exec(curFrame.document.baseURI) && - !curFrame.document.baseURI.startsWith(url)) { + } else if (doc.readyState == "interactive" && + aboutErrorRegex.exec(doc.baseURI) && + !doc.baseURI.startsWith(url)) { // We have reached an error url without requesting it. callback(); sendError(new UnknownError("Error loading page"), command_id); - } else if (curFrame.document.readyState == "interactive" && - curFrame.document.baseURI.startsWith("about:")) { + } else if (doc.readyState == "interactive" && + doc.baseURI.startsWith("about:")) { callback(); sendOk(command_id); } else { @@ -1309,7 +1314,7 @@ function get(msg) { // the current window (i.e. someone has used switch_to_frame). onDOMContentLoaded = function onDOMContentLoaded(event) { if (!event.originalTarget.defaultView.frameElement || - event.originalTarget.defaultView.frameElement == curFrame.frameElement) { + event.originalTarget.defaultView.frameElement == curContainer.frame.frameElement) { pollForReadyState(msg, start, () => { removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); onDOMContentLoaded = null; @@ -1325,7 +1330,7 @@ function get(msg) { navTimer.initWithCallback(timerFunc, msg.json.pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT); } addEventListener("DOMContentLoaded", onDOMContentLoaded, false); - curFrame.location = msg.json.url; + curContainer.frame.location = msg.json.url; } /** @@ -1345,7 +1350,7 @@ function cancelRequest() { */ function getCurrentUrl(isB2G) { if (isB2G) { - return curFrame.location.href; + return curContainer.frame.location.href; } else { return content.location.href; } @@ -1355,15 +1360,15 @@ function getCurrentUrl(isB2G) { * Get the title of the current browsing context. */ function getTitle() { - return curFrame.top.document.title; + return curContainer.frame.top.document.title; } /** * Get source of the current browsing context's DOM. */ function getPageSource() { - let XMLSerializer = curFrame.XMLSerializer; - let source = new XMLSerializer().serializeToString(curFrame.document); + let XMLSerializer = curContainer.frame.XMLSerializer; + let source = new XMLSerializer().serializeToString(curContainer.frame.document); return source; } @@ -1372,14 +1377,14 @@ function getPageSource() { * of the current top-level browsing context. */ function goBack() { - curFrame.history.back(); + curContainer.frame.history.back(); } /** * Go forward in history */ function goForward(msg) { - curFrame.history.forward(); + curContainer.frame.history.forward(); sendOk(msg.json.command_id); } @@ -1388,7 +1393,7 @@ function goForward(msg) { */ function refresh(msg) { let command_id = msg.json.command_id; - curFrame.location.reload(true); + curContainer.frame.location.reload(true); let listen = function() { removeEventListener("DOMContentLoaded", arguments.callee, false); sendOk(command_id); @@ -1403,7 +1408,7 @@ function refresh(msg) { function findElementContent(opts) { return new Promise((resolve, reject) => { elementManager.find( - curFrame, + curContainer, opts, opts.searchTimeout, false /* all */, @@ -1419,7 +1424,7 @@ function findElementContent(opts) { function findElementsContent(opts) { return new Promise((resolve, reject) => { elementManager.find( - curFrame, + curContainer, opts, opts.searchTimeout, true /* all */, @@ -1435,7 +1440,7 @@ function findElementsContent(opts) { * Reference to web element. */ function getActiveElement() { - let el = curFrame.document.activeElement; + let el = curContainer.frame.document.activeElement; return elementManager.addToKnownElements(el); } @@ -1446,7 +1451,7 @@ function getActiveElement() { * Reference to the web element to click. */ function clickElement(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let acc = accessibility.getAccessibleObject(el, true); let visible = checkVisible(el); checkVisibleAccessibility(acc, visible); @@ -1474,7 +1479,7 @@ function clickElement(id) { * The value of the attribute. */ function getElementAttribute(id, name) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); return utils.getElementAttribute(el, name); } @@ -1488,7 +1493,7 @@ function getElementAttribute(id, name) { * Text of element. */ function getElementText(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); return utils.getElementText(el); } @@ -1502,7 +1507,7 @@ function getElementText(id) { * Tag name of element. */ function getElementTagName(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); return el.tagName.toLowerCase(); } @@ -1513,7 +1518,7 @@ function getElementTagName(id) { * capability. */ function isElementDisplayed(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let displayed = utils.isElementDisplayed(el); checkVisibleAccessibility(accessibility.getAccessibleObject(el), displayed); return displayed; @@ -1532,8 +1537,8 @@ function isElementDisplayed(id) { * Effective value of the requested CSS property. */ function getElementValueOfCssProperty(id, prop) { - let el = elementManager.getKnownElement(id, curFrame); - let st = curFrame.document.defaultView.getComputedStyle(el, null); + let el = elementManager.getKnownElement(id, curContainer); + let st = curContainer.frame.document.defaultView.getComputedStyle(el, null); return st.getPropertyValue(prop); } @@ -1547,7 +1552,7 @@ function getElementValueOfCssProperty(id, prop) { * The width/height dimensions of th element. */ function getElementSize(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let clientRect = el.getBoundingClientRect(); return {width: clientRect.width, height: clientRect.height}; } @@ -1562,11 +1567,11 @@ function getElementSize(id) { * The x, y, width, and height properties of the element. */ function getElementRect(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let clientRect = el.getBoundingClientRect(); return { - x: clientRect.x + curFrame.pageXOffset, - y: clientRect.y + curFrame.pageYOffset, + x: clientRect.x + curContainer.frame.pageXOffset, + y: clientRect.y + curContainer.frame.pageYOffset, width: clientRect.width, height: clientRect.height }; @@ -1582,7 +1587,7 @@ function getElementRect(id) { * True if enabled, false otherwise. */ function isElementEnabled(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let enabled = utils.isElementEnabled(el); checkEnabledAccessibility( accessibility.getAccessibleObject(el), el, enabled); @@ -1596,7 +1601,7 @@ function isElementEnabled(id) { * and Radio Button states, or option elements. */ function isElementSelected(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let selected = utils.isElementSelected(el); checkSelectedAccessibility(accessibility.getAccessibleObject(el), selected); return selected; @@ -1610,7 +1615,7 @@ function sendKeysToElement(msg) { let val = msg.json.value; try { - let el = elementManager.getKnownElement(msg.json.id, curFrame); + let el = elementManager.getKnownElement(msg.json.id, curContainer); // Element should be actionable from the accessibility standpoint to be able // to send keys to it. checkActionableAccessibility(accessibility.getAccessibleObject(el, true)); @@ -1623,7 +1628,7 @@ function sendKeysToElement(msg) { sendSyncMessage("Marionette:getFiles", {value: p, command_id: command_id}); } else { - utils.sendKeysToElement(curFrame, el, val, sendOk, sendError, command_id); + utils.sendKeysToElement(curContainer.frame, el, val, sendOk, sendError, command_id); } } catch (e) { sendError(e, command_id); @@ -1634,7 +1639,7 @@ function sendKeysToElement(msg) { * Get the element's top left-hand corner point. */ function getElementLocation(id) { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); let rect = el.getBoundingClientRect(); return {x: rect.left, y: rect.top}; } @@ -1644,7 +1649,7 @@ function getElementLocation(id) { */ function clearElement(id) { try { - let el = elementManager.getKnownElement(id, curFrame); + let el = elementManager.getKnownElement(id, curContainer); if (el.type == "file") { el.value = null; } else { @@ -1661,6 +1666,34 @@ function clearElement(id) { } } +/** + * Switch the current context to the specified host's Shadow DOM. + * @param {WebElement} id + * Reference to web element. + */ +function switchToShadowRoot(id) { + if (!id) { + // If no host element is passed, attempt to find a parent shadow root or, if + // none found, unset the current shadow root + if (curContainer.shadowRoot) { + let parent = curContainer.shadowRoot.host; + while (parent && !(parent instanceof curContainer.frame.ShadowRoot)) { + parent = parent.parentNode; + } + curContainer.shadowRoot = parent; + } + return; + } + + let foundShadowRoot; + let hostEl = elementManager.getKnownElement(id, curContainer); + foundShadowRoot = hostEl.shadowRoot; + if (!foundShadowRoot) { + throw new NoSuchElementError('Unable to locate shadow root: ' + id); + } + curContainer.shadowRoot = foundShadowRoot; +} + /** * Switch to frame given either the server-assigned element id, * its index in window.frames, or the iframe's name or id. @@ -1669,11 +1702,11 @@ function switchToFrame(msg) { let command_id = msg.json.command_id; function checkLoad() { let errorRegex = /about:.+(error)|(blocked)\?/; - if (curFrame.document.readyState == "complete") { + if (curContainer.frame.document.readyState == "complete") { sendOk(command_id); return; - } else if (curFrame.document.readyState == "interactive" && - errorRegex.exec(curFrame.document.baseURI)) { + } else if (curContainer.frame.document.readyState == "interactive" && + errorRegex.exec(curContainer.frame.document.baseURI)) { sendError(new UnknownError("Error loading page"), command_id); return; } @@ -1682,12 +1715,12 @@ function switchToFrame(msg) { let foundFrame = null; let frames = []; let parWindow = null; - // Check of the curFrame reference is dead + // Check of the curContainer.frame reference is dead try { - frames = curFrame.frames; + frames = curContainer.frame.frames; //Until Bug 761935 lands, we won't have multiple nested OOP iframes. We will only have one. //parWindow will refer to the iframe above the nested OOP frame. - parWindow = curFrame.QueryInterface(Ci.nsIInterfaceRequestor) + parWindow = curContainer.frame.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; } catch (e) { // We probably have a dead compartment so accessing it is going to make Firefox @@ -1701,9 +1734,9 @@ function switchToFrame(msg) { // returning to root frame sendSyncMessage("Marionette:switchedToFrame", { frameValue: null }); - curFrame = content; + curContainer.frame = content; if(msg.json.focus == true) { - curFrame.focus(); + curContainer.frame.focus(); } checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); @@ -1713,7 +1746,7 @@ function switchToFrame(msg) { if (elementManager.seenItems[msg.json.element] != undefined) { let wantedFrame; try { - wantedFrame = elementManager.getKnownElement(msg.json.element, curFrame); //Frame Element + wantedFrame = elementManager.getKnownElement(msg.json.element, curContainer); //Frame Element } catch (e) { sendError(e, command_id); } @@ -1722,7 +1755,7 @@ function switchToFrame(msg) { for (let i = 0; i < frames.length; i++) { // use XPCNativeWrapper to compare elements; see bug 834266 if (XPCNativeWrapper(frames[i].frameElement) == XPCNativeWrapper(wantedFrame)) { - curFrame = frames[i].frameElement; + curContainer.frame = frames[i].frameElement; foundFrame = i; } } @@ -1731,10 +1764,10 @@ function switchToFrame(msg) { // Either the frame has been removed or we have a OOP frame // so lets just get all the iframes and do a quick loop before // throwing in the towel - let iframes = curFrame.document.getElementsByTagName("iframe"); + let iframes = curContainer.frame.document.getElementsByTagName("iframe"); for (var i = 0; i < iframes.length; i++) { if (XPCNativeWrapper(iframes[i]) == XPCNativeWrapper(wantedFrame)) { - curFrame = iframes[i]; + curContainer.frame = iframes[i]; foundFrame = i; } } @@ -1746,16 +1779,16 @@ function switchToFrame(msg) { try { foundFrame = frames[msg.json.id].frameElement; if (foundFrame !== null) { - curFrame = foundFrame; - foundFrame = elementManager.addToKnownElements(curFrame); + curContainer.frame = foundFrame; + foundFrame = elementManager.addToKnownElements(curContainer.frame); } else { // If foundFrame is null at this point then we have the top level browsing // context so should treat it accordingly. sendSyncMessage("Marionette:switchedToFrame", { frameValue: null}); - curFrame = content; + curContainer.frame = content; if(msg.json.focus == true) { - curFrame.focus(); + curContainer.frame.focus(); } checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); @@ -1765,9 +1798,9 @@ function switchToFrame(msg) { // Since window.frames does not return OOP frames it will throw // and we land up here. Let's not give up and check if there are // iframes and switch to the indexed frame there - let iframes = curFrame.document.getElementsByTagName("iframe"); + let iframes = curContainer.frame.document.getElementsByTagName("iframe"); if (msg.json.id >= 0 && msg.json.id < iframes.length) { - curFrame = iframes[msg.json.id]; + curContainer.frame = iframes[msg.json.id]; foundFrame = msg.json.id; } } @@ -1781,19 +1814,19 @@ function switchToFrame(msg) { // send a synchronous message to let the server update the currently active // frame element (for getActiveFrame) - let frameValue = elementManager.wrapValue(curFrame.wrappedJSObject)['ELEMENT']; + let frameValue = elementManager.wrapValue(curContainer.frame.wrappedJSObject)['ELEMENT']; sendSyncMessage("Marionette:switchedToFrame", { frameValue: frameValue }); let rv = null; - if (curFrame.contentWindow === null) { + if (curContainer.frame.contentWindow === null) { // The frame we want to switch to is a remote/OOP frame; // notify our parent to handle the switch - curFrame = content; + curContainer.frame = content; rv = {win: parWindow, frame: foundFrame}; } else { - curFrame = curFrame.contentWindow; + curContainer.frame = curContainer.frame.contentWindow; if (msg.json.focus) - curFrame.focus(); + curContainer.frame.focus(); checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); } @@ -1812,10 +1845,10 @@ function addCookie(msg) { } if (!cookie.domain) { - var location = curFrame.document.location; + var location = curContainer.frame.document.location; cookie.domain = location.hostname; } else { - var currLocation = curFrame.location; + var currLocation = curContainer.frame.location; var currDomain = currLocation.host; if (currDomain.indexOf(cookie.domain) == -1) { sendError(new InvalidCookieDomainError("You may only set cookies for the current domain"), msg.json.command_id); @@ -1829,7 +1862,7 @@ function addCookie(msg) { cookie.domain = cookie.domain.replace(/:\d+$/, ''); } - var document = curFrame.document; + var document = curContainer.frame.document; if (!document || !document.contentType.match(/html/i)) { sendError(new UnableToSetCookieError("You may only set cookies on html documents"), msg.json.command_id); } @@ -1847,7 +1880,7 @@ function addCookie(msg) { */ function getCookies(msg) { var toReturn = []; - var cookies = getVisibleCookies(curFrame.location); + var cookies = getVisibleCookies(curContainer.frame.location); for (let cookie of cookies) { var expires = cookie.expires; if (expires == 0) { // Session cookie, don't return an expiry. @@ -1873,7 +1906,7 @@ function getCookies(msg) { */ function deleteCookie(msg) { let toDelete = msg.json.name; - let cookies = getVisibleCookies(curFrame.location); + let cookies = getVisibleCookies(curContainer.frame.location); for (let cookie of cookies) { if (cookie.name == toDelete) { let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); @@ -1891,7 +1924,7 @@ function deleteCookie(msg) { * Delete all the visibile cookies on a page */ function deleteAllCookies(msg) { - let cookies = getVisibleCookies(curFrame.location); + let cookies = getVisibleCookies(curContainer.frame.location); for (let cookie of cookies) { let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); if (!deleted[0]) { @@ -1913,7 +1946,7 @@ function getVisibleCookies(location) { } function getAppCacheStatus(msg) { - sendResponse({ value: curFrame.applicationCache.status }, + sendResponse({ value: curContainer.frame.applicationCache.status }, msg.json.command_id); } @@ -1988,7 +2021,7 @@ function takeScreenshot(msg) { let node = null; if (msg.json.id) { try { - node = elementManager.getKnownElement(msg.json.id, curFrame) + node = elementManager.getKnownElement(msg.json.id, curContainer) } catch (e) { sendResponse(e.message, e.code, e.stack, msg.json.command_id); @@ -1996,14 +2029,14 @@ function takeScreenshot(msg) { } } else { - node = curFrame; + node = curContainer.frame; } let highlights = msg.json.highlights; - var document = curFrame.document; + var document = curContainer.frame.document; var rect, win, width, height, left, top; // node can be either a window or an arbitrary DOM node - if (node == curFrame) { + if (node == curContainer.frame) { // node is a window win = node; if (msg.json.full) { @@ -2017,8 +2050,8 @@ function takeScreenshot(msg) { // only the viewport width = document.documentElement.clientWidth; height = document.documentElement.clientHeight; - left = curFrame.pageXOffset; - top = curFrame.pageYOffset; + left = curContainer.frame.pageXOffset; + top = curContainer.frame.pageYOffset; } } else { @@ -2047,7 +2080,7 @@ function takeScreenshot(msg) { ctx.save(); for (var i = 0; i < highlights.length; ++i) { - var elem = elementManager.getKnownElement(highlights[i], curFrame); + var elem = elementManager.getKnownElement(highlights[i], curContainer); rect = elem.getBoundingClientRect(); var offsetY = -top; From 69574050d1b6fe5963c58be6e7157863a38dc2e7 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Mon, 24 Aug 2015 15:40:57 -0700 Subject: [PATCH 22/85] Bug 1196079 - Always try to release Notification via normal WorkerRunnable first. r=wchen --- dom/notification/Notification.cpp | 85 ++++++++++++++----------------- dom/notification/Notification.h | 22 +++----- 2 files changed, 44 insertions(+), 63 deletions(-) diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 868378851a5..82bfbef3aec 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -409,6 +409,22 @@ public: } }; +class ReleaseNotificationRunnable final : public NotificationWorkerRunnable +{ + Notification* mNotification; +public: + explicit ReleaseNotificationRunnable(Notification* aNotification) + : NotificationWorkerRunnable(aNotification->mWorkerPrivate) + , mNotification(aNotification) + {} + + void + WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + mNotification->ReleaseObject(); + } +}; + // Create one whenever you require ownership of the notification. Use with // UniquePtr<>. See Notification.h for details. class NotificationRef final { @@ -452,18 +468,31 @@ public: ~NotificationRef() { if (Initialized() && mNotification) { - if (mNotification->mWorkerPrivate && NS_IsMainThread()) { - nsRefPtr r = - new ReleaseNotificationControlRunnable(mNotification); - AutoSafeJSContext cx; - if (!r->Dispatch(cx)) { - MOZ_CRASH("Will leak worker thread Notification!"); + Notification* notification = mNotification; + mNotification = nullptr; + if (notification->mWorkerPrivate && NS_IsMainThread()) { + // Try to pass ownership back to the worker. If the dispatch succeeds we + // are guaranteed this runnable will run, and that it will run after queued + // event runnables, so event runnables will have a safe pointer to the + // Notification. + // + // If the dispatch fails, the worker isn't running anymore and the event + // runnables have already run or been canceled. We can use a control + // runnable to release the reference. + nsRefPtr r = + new ReleaseNotificationRunnable(notification); + + AutoJSAPI jsapi; + jsapi.Init(); + if (!r->Dispatch(jsapi.cx())) { + nsRefPtr r = + new ReleaseNotificationControlRunnable(notification); + MOZ_ALWAYS_TRUE(r->Dispatch(jsapi.cx())); } } else { - mNotification->AssertIsOnTargetThread(); - mNotification->ReleaseObject(); + notification->AssertIsOnTargetThread(); + notification->ReleaseObject(); } - mNotification = nullptr; } } @@ -973,44 +1002,6 @@ protected: AssertIsOnMainThread(); MOZ_ASSERT(mNotificationRef); - Notification* notification = mNotificationRef->GetNotification(); - if (!notification) { - return; - } - - // Try to pass ownership back to the worker. If the dispatch succeeds we - // are guaranteed this runnable will run, and that it will run after queued - // event runnables, so event runnables will have a safe pointer to the - // Notification. - // - // If the dispatch fails, the worker isn't running anymore and the event - // runnables have already run. We can just let the standard NotificationRef - // release routine take over when ReleaseNotificationRunnable gets deleted. - class ReleaseNotificationRunnable final : public NotificationWorkerRunnable - { - UniquePtr mNotificationRef; - public: - explicit ReleaseNotificationRunnable(UniquePtr aRef) - : NotificationWorkerRunnable(aRef->GetNotification()->mWorkerPrivate) - , mNotificationRef(Move(aRef)) - {} - - void - WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - UniquePtr ref; - mozilla::Swap(ref, mNotificationRef); - // Gets released at the end of the function. - } - }; - - nsRefPtr r = - new ReleaseNotificationRunnable(Move(mNotificationRef)); - notification = nullptr; - - AutoJSAPI jsapi; - jsapi.Init(); - r->Dispatch(jsapi.cx()); } }; diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index dd2825d349f..5153a5b5ac9 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -89,22 +89,12 @@ public: * Note that the Notification's JS wrapper does it's standard * AddRef()/Release() and is not affected by any of this. * - * There is one case related to the WorkerNotificationObserver having to - * dispatch WorkerRunnables to the worker thread which will use the - * Notification object. We can end up in a situation where an event runnable is - * dispatched to the worker, gets queued in the worker's event queue, but then, - * the worker yields to the main thread. Here the main thread observer is - * destroyed, which frees its NotificationRef. The NotificationRef dispatches - * a ControlRunnable to the worker, which runs before the event runnable, - * leading to the event runnable possibly not having a valid Notification - * reference. - * We solve this problem by having WorkerNotificationObserver's dtor - * dispatching a standard WorkerRunnable to do the release (this guarantees the - * ordering of the release is after the event runnables). All WorkerRunnables - * that get dispatched successfully are guaranteed to run on the worker before - * it shuts down. If that dispatch fails, the standard ControlRunnable based - * shutdown is acceptable since the already dispatched event runnables have - * already run or canceled (the worker is already past Running). + * Since the worker event queue can have runnables that will dispatch events on + * the Notification, the NotificationRef destructor will first try to release + * the Notification by dispatching a normal runnable to the worker so that it is + * queued after any event runnables. If that dispatch fails, it means the worker + * is no longer running and queued WorkerRunnables will be canceled, so we + * dispatch a control runnable instead. * */ class Notification : public DOMEventTargetHelper From 40122c5ef1883ba5210318a383f7fab34b21da46 Mon Sep 17 00:00:00 2001 From: Doug Turner Date: Mon, 31 Aug 2015 12:28:04 -0700 Subject: [PATCH 23/85] Bug 1164432. Try disabling test_try_registering_offline_disabled due to too many intermittent failures. r=kitcambridge --- dom/push/test/mochitest.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dom/push/test/mochitest.ini b/dom/push/test/mochitest.ini index 732f9a1d3b9..c14fef20265 100644 --- a/dom/push/test/mochitest.ini +++ b/dom/push/test/mochitest.ini @@ -19,5 +19,6 @@ skip-if = os == "android" || toolkit == "gonk" skip-if = os == "android" || toolkit == "gonk" [test_multiple_register_different_scope.html] skip-if = os == "android" || toolkit == "gonk" -[test_try_registering_offline_disabled.html] -skip-if = os == "android" || toolkit == "gonk" +# Disabled for too many intermittent failures (bug 1164432) +# [test_try_registering_offline_disabled.html] +# skip-if = os == "android" || toolkit == "gonk" From c949250be2012d7dac22e632f098405851ad18c9 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Fri, 28 Aug 2015 19:13:00 -0400 Subject: [PATCH 24/85] Bug 1199794 - Add NULL checks to avoid crashing during media playback if the audio device removed. r=padenot --- media/libcubeb/src/cubeb_wasapi.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp index 7dbf202a987..2ba8509bb72 100644 --- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -701,6 +701,13 @@ current_stream_delay(cubeb_stream * stm) { stm->stream_reset_lock->assert_current_thread_owns(); + /* If the default audio endpoint went away during playback and we weren't + able to configure a new one, it's possible the caller may call this + before the error callback has propogated back. */ + if (!stm->audio_clock) { + return 0; + } + UINT64 freq; HRESULT hr = stm->audio_clock->GetFrequency(&freq); if (FAILED(hr)) { @@ -728,6 +735,10 @@ stream_set_volume(cubeb_stream * stm, float volume) { stm->stream_reset_lock->assert_current_thread_owns(); + if (!stm->audio_stream_volume) { + return CUBEB_ERROR; + } + uint32_t channels; HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels); if (hr != S_OK) { @@ -1289,6 +1300,10 @@ int wasapi_stream_start(cubeb_stream * stm) XASSERT(stm && !stm->thread && !stm->shutdown_event); + if (!stm->client) { + return CUBEB_ERROR; + } + HRESULT hr = stm->client->Start(); if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { LOG("audioclient invalid device, reconfiguring\n", hr); From 6172dfc85d04f7e4776fb9556a90e5467ed2519d Mon Sep 17 00:00:00 2001 From: Heiher Date: Wed, 26 Aug 2015 07:05:00 -0400 Subject: [PATCH 25/85] Bug 1198732 - IonMonkey: MIPS32: Fix calculate frame size in generateEnterJIT. r=nbp --- js/src/jit/mips32/Trampoline-mips32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit/mips32/Trampoline-mips32.cpp b/js/src/jit/mips32/Trampoline-mips32.cpp index d57865c0a08..075f9491e89 100644 --- a/js/src/jit/mips32/Trampoline-mips32.cpp +++ b/js/src/jit/mips32/Trampoline-mips32.cpp @@ -314,7 +314,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type) // Pop arguments off the stack. // s0 <- 8*argc (size of all arguments we pushed on the stack) masm.pop(s0); - masm.rshiftPtr(Imm32(4), s0); + masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), s0); masm.addPtr(s0, StackPointer); // Store the returned value into the slotVp From 6007e8faaa976aa1c962acdaebfb9926e7deb113 Mon Sep 17 00:00:00 2001 From: Henry Chang Date: Mon, 31 Aug 2015 17:47:10 +0800 Subject: [PATCH 26/85] Bug 1195713 - Set the proper notification callbacks for the inner channel. r=valentin --- netwerk/protocol/http/PackagedAppService.cpp | 7 +++++++ netwerk/test/unit/test_packaged_app_channel.js | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index 1235b264c51..d3d4c0e586e 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -16,6 +16,7 @@ #include "mozilla/Logging.h" #include "mozilla/DebugOnly.h" #include "nsIHttpHeaderVisitor.h" +#include "mozilla/LoadContext.h" namespace mozilla { namespace net { @@ -811,6 +812,12 @@ PackagedAppService::GetResource(nsIChannel *aChannel, nsRefPtr listener = new PackagedAppChannelListener(downloader, mimeConverter); + nsCOMPtr loadContext; + aChannel->GetNotificationCallbacks(getter_AddRefs(loadContext)); + if (loadContext) { + channel->SetNotificationCallbacks(loadContext); + } + if (loadInfo && loadInfo->GetEnforceSecurity()) { return channel->AsyncOpen2(listener); } diff --git a/netwerk/test/unit/test_packaged_app_channel.js b/netwerk/test/unit/test_packaged_app_channel.js index 2bffa617dce..1ea03b83355 100644 --- a/netwerk/test/unit/test_packaged_app_channel.js +++ b/netwerk/test/unit/test_packaged_app_channel.js @@ -123,14 +123,20 @@ function run_test() do_register_cleanup(reset_pref); add_test(test_channel); + add_test(test_channel_no_notificationCallbacks); add_test(test_channel_uris); // run tests run_next_test(); } -function test_channel() { +function test_channel(aNullNotificationCallbacks) { var channel = make_channel(uri+"/package!//index.html"); + + if (!aNullNotificationCallbacks) { + channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false); + } + channel.asyncOpen(new Listener(function(l) { // XXX: no content length available for this resource //do_check_true(channel.contentLength > 0); @@ -140,6 +146,10 @@ function test_channel() { }), null); } +function test_channel_no_notificationCallbacks() { + test_channel(true); +} + function test_channel_uris() { // A `!//` in the query or ref should not be handled as a packaged app resource var channel = make_channel(uri+"/regular?bla!//bla#bla!//bla"); From d2e09bf45486e419f773e64c028b26bdeaf075a8 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Fri, 28 Aug 2015 09:16:00 -0400 Subject: [PATCH 27/85] Bug 1181317 - Switch drawing order of filter feBlend inputs. r=mstange --- gfx/src/FilterSupport.cpp | 6 +++-- layout/reftests/svg/filters/feBlend-1-ref.svg | 25 ++++++++++--------- layout/reftests/svg/filters/feBlend-1.svg | 8 +++--- layout/reftests/svg/filters/feBlend-2-ref.svg | 1 + layout/reftests/svg/filters/feBlend-2.svg | 8 ++++++ layout/reftests/svg/filters/reftest.list | 2 +- 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/gfx/src/FilterSupport.cpp b/gfx/src/FilterSupport.cpp index 83ed3e84045..3b6de434f0f 100644 --- a/gfx/src/FilterSupport.cpp +++ b/gfx/src/FilterSupport.cpp @@ -747,8 +747,10 @@ FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescriptio BLEND_MODE_LUMINOSITY }; filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]); - filter->SetInput(IN_BLEND_IN, aSources[0]); - filter->SetInput(IN_BLEND_IN2, aSources[1]); + // The correct input order for both software and D2D filters is flipped + // from our source order, so flip here. + filter->SetInput(IN_BLEND_IN, aSources[1]); + filter->SetInput(IN_BLEND_IN2, aSources[0]); } return filter.forget(); } diff --git a/layout/reftests/svg/filters/feBlend-1-ref.svg b/layout/reftests/svg/filters/feBlend-1-ref.svg index 9f6c26f77b7..24a6fdfd6a3 100644 --- a/layout/reftests/svg/filters/feBlend-1-ref.svg +++ b/layout/reftests/svg/filters/feBlend-1-ref.svg @@ -1,19 +1,20 @@ - - + + - + - - - - - + + + + + - - - - + + + + + diff --git a/layout/reftests/svg/filters/feBlend-1.svg b/layout/reftests/svg/filters/feBlend-1.svg index f467afb6583..6a94853f5b8 100644 --- a/layout/reftests/svg/filters/feBlend-1.svg +++ b/layout/reftests/svg/filters/feBlend-1.svg @@ -52,7 +52,7 @@ - + @@ -95,10 +95,10 @@ - + - - \ No newline at end of file + + diff --git a/layout/reftests/svg/filters/feBlend-2-ref.svg b/layout/reftests/svg/filters/feBlend-2-ref.svg index f60dca7bd84..6b62e6a2708 100644 --- a/layout/reftests/svg/filters/feBlend-2-ref.svg +++ b/layout/reftests/svg/filters/feBlend-2-ref.svg @@ -1,5 +1,6 @@ + diff --git a/layout/reftests/svg/filters/feBlend-2.svg b/layout/reftests/svg/filters/feBlend-2.svg index fc7787845a3..c03e665d207 100644 --- a/layout/reftests/svg/filters/feBlend-2.svg +++ b/layout/reftests/svg/filters/feBlend-2.svg @@ -6,4 +6,12 @@ + + + + + + + diff --git a/layout/reftests/svg/filters/reftest.list b/layout/reftests/svg/filters/reftest.list index 0fde197fdfd..3a623f22c7c 100644 --- a/layout/reftests/svg/filters/reftest.list +++ b/layout/reftests/svg/filters/reftest.list @@ -18,7 +18,7 @@ include svg-filter-chains/reftest.list == dynamic-filter-invalidation-01.svg pass.svg == dynamic-filter-invalidation-02.svg pass.svg -fuzzy(1,40000) == feBlend-1.svg feBlend-1-ref.svg +fuzzy(1,42500) == feBlend-1.svg feBlend-1-ref.svg == feBlend-2.svg feBlend-2-ref.svg fuzzy-if(d2d,1,6400) == feColorMatrix-1.svg feColorMatrix-1-ref.svg From a52efb27330e0040aeb34e554e9a842c3269b222 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 12:50:20 -0400 Subject: [PATCH 28/85] Bug 1200208 - Send the audio-playback notification when the page calls HTMLMediaElement::Play() before the metadata has been fully loaded; r=baku --- dom/base/test/mochitest.ini | 2 + .../test_audioNotificationWithEarlyPlay.html | 73 +++++++++++++++++++ dom/html/HTMLMediaElement.cpp | 69 +++++++++++++++--- dom/html/HTMLMediaElement.h | 16 ++-- 4 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 dom/base/test/test_audioNotificationWithEarlyPlay.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 9a102c6b6f9..e8023134bc8 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,6 +267,8 @@ support-files = skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' +[test_audioNotificationWithEarlyPlay.html] +skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] diff --git a/dom/base/test/test_audioNotificationWithEarlyPlay.html b/dom/base/test/test_audioNotificationWithEarlyPlay.html new file mode 100644 index 00000000000..66184ae03ab --- /dev/null +++ b/dom/base/test/test_audioNotificationWithEarlyPlay.html @@ -0,0 +1,73 @@ + + + + Test for audio controller in windows + + + + +
+
+ + + + + diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 2e47c094648..540feb3e434 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -116,6 +116,34 @@ static PRLogModuleInfo* gMediaElementEventsLog; using namespace mozilla::layers; using mozilla::net::nsMediaFragmentURIParser; +class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent +{ + nsRefPtr mElement; + bool mShouldNotify; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; +public: + AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement, + bool aNotify + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mElement(aElement) + , mShouldNotify(aNotify) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (mShouldNotify) { + mElement->NotifyAudioChannelAgent(false); + } + } + ~AutoNotifyAudioChannelAgent() + { + if (mShouldNotify) { + // The audio channel agent is destroyed at this point. + if (mElement->MaybeCreateAudioChannelAgent()) { + mElement->NotifyAudioChannelAgent(true); + } + } + } +}; + namespace mozilla { namespace dom { @@ -3189,6 +3217,14 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, { MOZ_ASSERT(NS_IsMainThread()); + // If the element is gaining or losing an audio track, we need to notify + // the audio channel agent so that the correct audio-playback events will + // get dispatched. + bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio(); + AutoNotifyAudioChannelAgent autoNotify(this, + audioTrackChanging && + mPlayingThroughTheAudioChannel); + mMediaInfo = *aInfo; mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME @@ -4488,7 +4524,25 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) return NS_OK; } -void HTMLMediaElement::UpdateAudioChannelPlayingState() +bool +HTMLMediaElement::MaybeCreateAudioChannelAgent() +{ + if (!mAudioChannelAgent) { + nsresult rv; + mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + MOZ_ASSERT(mAudioChannelAgent); + mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), + static_cast(mAudioChannel), + this); + } + return true; +} + +void +HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = (!mPaused && @@ -4506,18 +4560,9 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState() return; } - if (!mAudioChannelAgent) { - nsresult rv; - mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); - if (!mAudioChannelAgent) { - return; - } - mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), - static_cast(mAudioChannel), - this); + if (MaybeCreateAudioChannelAgent()) { + NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } - - NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 664b508dc4b..410168beaed 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,10 +35,6 @@ // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ -class nsIChannel; -class nsIHttpChannel; -class nsILoadGroup; - typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; @@ -56,9 +52,13 @@ class MediaTrack; } // namespace dom } // namespace mozilla +class AutoNotifyAudioChannelAgent; +class nsIChannel; +class nsIHttpChannel; +class nsILoadGroup; +class nsIRunnable; class nsITimer; class nsRange; -class nsIRunnable; namespace mozilla { namespace dom { @@ -78,6 +78,8 @@ class HTMLMediaElement : public nsGenericHTMLElement, public MediaDecoderOwner, public nsIAudioChannelAgentCallback { + friend class AutoNotifyAudioChannelAgent; + public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::layers::ImageContainer ImageContainer; @@ -1050,6 +1052,10 @@ protected: // Notifies the audio channel agent when the element starts or stops playing. void NotifyAudioChannelAgent(bool aPlaying); + // Creates the audio channel agent if needed. Returns true if the audio + // channel agent is ready to be used. + bool MaybeCreateAudioChannelAgent(); + class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. From 20036e1c22ae7a42e5ec4e32f16b9577131e6171 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 18 Aug 2015 14:57:07 -0400 Subject: [PATCH 29/85] bug 1194818 - h2 header priority handling r=hurley --- netwerk/protocol/http/Http2Session.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index c5311559e98..05dd3366f45 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -1152,9 +1152,12 @@ Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength if (mInputFrameFlags & kFlag_PADDED) { paddingLength = *reinterpret_cast(mInputFrameBuffer + kFrameHeaderBytes); paddingControlBytes = 1; + } else { + paddingLength = 0; + paddingControlBytes = 0; } - if (paddingLength > mInputFrameDataSize) { + if ((paddingLength + paddingControlBytes) > mInputFrameDataSize) { // This is fatal to the session LOG3(("Http2Session::ParsePadding %p stream 0x%x PROTOCOL_ERROR " "paddingLength %d > frame size %d\n", @@ -1212,6 +1215,11 @@ Http2Session::RecvHeaders(Http2Session *self) paddingLength, self->mInputFrameFlags & kFlag_PADDED)); + if ((paddingControlBytes + priorityLen + paddingLength) > self->mInputFrameDataSize) { + // This is fatal to the session + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); + } + if (!self->mInputFrameDataStream) { // Cannot find stream. We can continue the session, but we need to // uncompress the header block to maintain the correct compression context From 4c61efd901bbf7f07877081206147f1f31db4bbf Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 18 Aug 2015 15:16:14 -0400 Subject: [PATCH 30/85] bug 1194820 - h2 push promise padding handling r=hurley --- netwerk/protocol/http/Http2Session.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index 05dd3366f45..b14ebaa5857 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -1565,11 +1565,11 @@ Http2Session::RecvPushPromise(Http2Session *self) self->mContinuedPromiseStream = promisedID; } - if (paddingLength > self->mInputFrameDataSize) { + if ((paddingControlBytes + promiseLen + paddingLength) > self->mInputFrameDataSize) { // This is fatal to the session LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X " - "PROTOCOL_ERROR paddingLength %d > frame size %d\n", - self, promisedID, associatedID, paddingLength, + "PROTOCOL_ERROR extra %d > frame size %d\n", + self, promisedID, associatedID, (paddingControlBytes + promiseLen + paddingLength), self->mInputFrameDataSize)); RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); } From 701c056e4d8fed90103cb673df13f078a9501711 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 31 Aug 2015 16:40:43 -0400 Subject: [PATCH 31/85] No bug - Fuzz more css-mediaqueries tests on Android. --- layout/reftests/css-mediaqueries/reftest.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/reftests/css-mediaqueries/reftest.list b/layout/reftests/css-mediaqueries/reftest.list index 6a389ecaede..8b84c980743 100644 --- a/layout/reftests/css-mediaqueries/reftest.list +++ b/layout/reftests/css-mediaqueries/reftest.list @@ -6,8 +6,8 @@ skip-if(B2G||Mulet) == mq_print_minheight.xhtml mq_print-ref.xhtml # bug 773482 skip-if(B2G||Mulet) == mq_print_aspectratio.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == mq_print_deviceaspectratio.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == mq_print_devicewidth.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop -skip-if(B2G||Mulet) == mq_print_orientation.xhtml mq_print_orientation-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop -skip-if(B2G||Mulet) == mq_print_maxheight.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(Android,8,454) skip-if(B2G||Mulet) == mq_print_orientation.xhtml mq_print_orientation-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop +fuzzy-if(Android,8,454) skip-if(B2G||Mulet) == mq_print_maxheight.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == mq_print_maxwidth.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) == mq_print_maxwidth_updown.xhtml mq_print-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop From 41fc7883c15ce921bfaa0949033771e53892a334 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 16:48:41 -0400 Subject: [PATCH 32/85] Backout bug 1200208 for build bustage on a CLOSED TREE --- dom/base/test/mochitest.ini | 2 - .../test_audioNotificationWithEarlyPlay.html | 73 ------------------- dom/html/HTMLMediaElement.cpp | 69 +++--------------- dom/html/HTMLMediaElement.h | 16 ++-- 4 files changed, 17 insertions(+), 143 deletions(-) delete mode 100644 dom/base/test/test_audioNotificationWithEarlyPlay.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index e8023134bc8..9a102c6b6f9 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,8 +267,6 @@ support-files = skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' -[test_audioNotificationWithEarlyPlay.html] -skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] diff --git a/dom/base/test/test_audioNotificationWithEarlyPlay.html b/dom/base/test/test_audioNotificationWithEarlyPlay.html deleted file mode 100644 index 66184ae03ab..00000000000 --- a/dom/base/test/test_audioNotificationWithEarlyPlay.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - Test for audio controller in windows - - - - -
-
- - - - - diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 540feb3e434..2e47c094648 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -116,34 +116,6 @@ static PRLogModuleInfo* gMediaElementEventsLog; using namespace mozilla::layers; using mozilla::net::nsMediaFragmentURIParser; -class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent -{ - nsRefPtr mElement; - bool mShouldNotify; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; -public: - AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement, - bool aNotify - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : mElement(aElement) - , mShouldNotify(aNotify) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (mShouldNotify) { - mElement->NotifyAudioChannelAgent(false); - } - } - ~AutoNotifyAudioChannelAgent() - { - if (mShouldNotify) { - // The audio channel agent is destroyed at this point. - if (mElement->MaybeCreateAudioChannelAgent()) { - mElement->NotifyAudioChannelAgent(true); - } - } - } -}; - namespace mozilla { namespace dom { @@ -3217,14 +3189,6 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, { MOZ_ASSERT(NS_IsMainThread()); - // If the element is gaining or losing an audio track, we need to notify - // the audio channel agent so that the correct audio-playback events will - // get dispatched. - bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio(); - AutoNotifyAudioChannelAgent autoNotify(this, - audioTrackChanging && - mPlayingThroughTheAudioChannel); - mMediaInfo = *aInfo; mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME @@ -4524,25 +4488,7 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) return NS_OK; } -bool -HTMLMediaElement::MaybeCreateAudioChannelAgent() -{ - if (!mAudioChannelAgent) { - nsresult rv; - mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - MOZ_ASSERT(mAudioChannelAgent); - mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), - static_cast(mAudioChannel), - this); - } - return true; -} - -void -HTMLMediaElement::UpdateAudioChannelPlayingState() +void HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = (!mPaused && @@ -4560,9 +4506,18 @@ HTMLMediaElement::UpdateAudioChannelPlayingState() return; } - if (MaybeCreateAudioChannelAgent()) { - NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); + if (!mAudioChannelAgent) { + nsresult rv; + mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); + if (!mAudioChannelAgent) { + return; + } + mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), + static_cast(mAudioChannel), + this); } + + NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 410168beaed..664b508dc4b 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,6 +35,10 @@ // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ +class nsIChannel; +class nsIHttpChannel; +class nsILoadGroup; + typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; @@ -52,13 +56,9 @@ class MediaTrack; } // namespace dom } // namespace mozilla -class AutoNotifyAudioChannelAgent; -class nsIChannel; -class nsIHttpChannel; -class nsILoadGroup; -class nsIRunnable; class nsITimer; class nsRange; +class nsIRunnable; namespace mozilla { namespace dom { @@ -78,8 +78,6 @@ class HTMLMediaElement : public nsGenericHTMLElement, public MediaDecoderOwner, public nsIAudioChannelAgentCallback { - friend class AutoNotifyAudioChannelAgent; - public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::layers::ImageContainer ImageContainer; @@ -1052,10 +1050,6 @@ protected: // Notifies the audio channel agent when the element starts or stops playing. void NotifyAudioChannelAgent(bool aPlaying); - // Creates the audio channel agent if needed. Returns true if the audio - // channel agent is ready to be used. - bool MaybeCreateAudioChannelAgent(); - class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. From d700e86ca9e273b918c2c130996f0d309679bb44 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Mon, 31 Aug 2015 14:26:19 -0700 Subject: [PATCH 33/85] Bug 1192467 - Formalize precondition that SetDisplayDocument must not be called with null. - r=peterv --- dom/base/nsIDocument.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 8e0ce8b95f2..a27694edd5c 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -1736,14 +1736,15 @@ public: */ void SetDisplayDocument(nsIDocument* aDisplayDocument) { - NS_PRECONDITION(!GetShell() && - !GetContainer() && - !GetWindow(), - "Shouldn't set mDisplayDocument on documents that already " - "have a presentation or a docshell or a window"); - NS_PRECONDITION(aDisplayDocument != this, "Should be different document"); - NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(), - "Display documents should not nest"); + MOZ_ASSERT(!GetShell() && + !GetContainer() && + !GetWindow(), + "Shouldn't set mDisplayDocument on documents that already " + "have a presentation or a docshell or a window"); + MOZ_ASSERT(aDisplayDocument, "Must not be null"); + MOZ_ASSERT(aDisplayDocument != this, "Should be different document"); + MOZ_ASSERT(!aDisplayDocument->GetDisplayDocument(), + "Display documents should not nest"); mDisplayDocument = aDisplayDocument; mHasDisplayDocument = !!aDisplayDocument; } @@ -1764,7 +1765,7 @@ public: virtual ~ExternalResourceLoad() {} void AddObserver(nsIObserver* aObserver) { - NS_PRECONDITION(aObserver, "Must have observer"); + MOZ_ASSERT(aObserver, "Must have observer"); mObservers.AppendElement(aObserver); } From cd54636a360b299d098d5246b7e23869d4922f89 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 34/85] Bug 1112071 Change Cache ManagerId to use QuotaManager's concept of origin. r=ehsan --- dom/cache/Manager.cpp | 2 +- dom/cache/ManagerId.cpp | 31 ++++++++++--------------------- dom/cache/ManagerId.h | 9 ++++----- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 239f82f33df..2b0cc98d2cb 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -400,7 +400,7 @@ private: while (iter.HasMore()) { nsRefPtr manager = iter.GetNext(); if (aOrigin.IsVoid() || - manager->mManagerId->ExtendedOrigin() == aOrigin) { + manager->mManagerId->QuotaOrigin() == aOrigin) { manager->Abort(); } } diff --git a/dom/cache/ManagerId.cpp b/dom/cache/ManagerId.cpp index 31d00560ed3..8d67aab6158 100644 --- a/dom/cache/ManagerId.cpp +++ b/dom/cache/ManagerId.cpp @@ -21,26 +21,16 @@ ManagerId::Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut) MOZ_ASSERT(NS_IsMainThread()); // The QuotaManager::GetInfoFromPrincipal() has special logic for system - // and about: principals. We currently don't need the system principal logic - // because ManagerId only uses the origin for in memory comparisons. We - // also don't do any special logic to host the same Cache for different about: - // pages, so we don't need those checks either. - // - // But, if we get the same QuotaManager directory for different about: - // origins, we probably only want one Manager instance. So, we might - // want to start using the QM's concept of origin uniqueness here. - // - // TODO: consider using QuotaManager's modified origin here (bug 1112071) - - nsCString origin; - nsresult rv = aPrincipal->GetOriginNoSuffix(origin); + // and about: principals. We need to use the same modified origin in + // order to interpret calls from QM correctly. + nsCString quotaOrigin; + nsresult rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, + nullptr, //group + "aOrigin, + nullptr); // is app if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - nsCString jarPrefix; - rv = aPrincipal->GetJarPrefix(jarPrefix); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - nsRefPtr ref = new ManagerId(aPrincipal, origin, jarPrefix); + nsRefPtr ref = new ManagerId(aPrincipal, quotaOrigin); ref.forget(aManagerIdOut); return NS_OK; @@ -54,10 +44,9 @@ ManagerId::Principal() const return ref.forget(); } -ManagerId::ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin, - const nsACString& aJarPrefix) +ManagerId::ManagerId(nsIPrincipal* aPrincipal, const nsACString& aQuotaOrigin) : mPrincipal(aPrincipal) - , mExtendedOrigin(aJarPrefix + aOrigin) + , mQuotaOrigin(aQuotaOrigin) { MOZ_ASSERT(mPrincipal); } diff --git a/dom/cache/ManagerId.h b/dom/cache/ManagerId.h index 6b735334169..445520bb725 100644 --- a/dom/cache/ManagerId.h +++ b/dom/cache/ManagerId.h @@ -29,16 +29,15 @@ public: // Main thread only already_AddRefed Principal() const; - const nsACString& ExtendedOrigin() const { return mExtendedOrigin; } + const nsACString& QuotaOrigin() const { return mQuotaOrigin; } bool operator==(const ManagerId& aOther) const { - return mExtendedOrigin == aOther.mExtendedOrigin; + return mQuotaOrigin == aOther.mQuotaOrigin; } private: - ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin, - const nsACString& aJarPrefix); + ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin); ~ManagerId(); ManagerId(const ManagerId&) = delete; @@ -48,7 +47,7 @@ private: nsCOMPtr mPrincipal; // immutable to allow threadsfe access - const nsCString mExtendedOrigin; + const nsCString mQuotaOrigin; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::cache::ManagerId) From 867558de7a0bbade3d93e1ee356d358d117c3774 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 35/85] Bug 1184607 P1 Update webidl for RequestRedirect and 'opaqueredirect' in ResponseType. r=ehsan --- dom/webidl/Request.webidl | 3 +++ dom/webidl/Response.webidl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dom/webidl/Request.webidl b/dom/webidl/Request.webidl index dfaf2c398c8..5774946d36e 100644 --- a/dom/webidl/Request.webidl +++ b/dom/webidl/Request.webidl @@ -23,6 +23,7 @@ interface Request { readonly attribute RequestMode mode; readonly attribute RequestCredentials credentials; readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; [Throws, NewObject] Request clone(); @@ -40,6 +41,7 @@ dictionary RequestInit { RequestMode mode; RequestCredentials credentials; RequestCache cache; + RequestRedirect redirect; }; // Gecko currently does not ship RequestContext, so please don't use it in IDL @@ -61,3 +63,4 @@ enum RequestContext { enum RequestMode { "same-origin", "no-cors", "cors", "cors-with-forced-preflight" }; enum RequestCredentials { "omit", "same-origin", "include" }; enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; +enum RequestRedirect { "follow", "error", "manual" }; diff --git a/dom/webidl/Response.webidl b/dom/webidl/Response.webidl index 2ebc78bc4b3..edc4a520d27 100644 --- a/dom/webidl/Response.webidl +++ b/dom/webidl/Response.webidl @@ -34,4 +34,4 @@ dictionary ResponseInit { HeadersInit headers; }; -enum ResponseType { "basic", "cors", "default", "error", "opaque" }; +enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" }; From c526c6fd5e7bcd2ab80607da721c71339c866f36 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 36/85] Bug 1184607 P2 Update Request and Response DOM objects for new redirect model. r=nsm --- dom/fetch/InternalRequest.cpp | 2 ++ dom/fetch/InternalRequest.h | 15 +++++++++++++++ dom/fetch/InternalResponse.cpp | 11 ++++++++++- dom/fetch/InternalResponse.h | 31 ++++++++++++++++++++++++++++--- dom/fetch/Request.cpp | 4 ++++ dom/fetch/Request.h | 6 ++++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index fcb40a7521f..b15386074cb 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -42,6 +42,7 @@ InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult copy->mMode = mMode; copy->mCredentialsMode = mCredentialsMode; copy->mCacheMode = mCacheMode; + copy->mRedirectMode = mRedirectMode; copy->mCreatedByFetchEvent = mCreatedByFetchEvent; return copy.forget(); } @@ -80,6 +81,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther) , mCredentialsMode(aOther.mCredentialsMode) , mResponseTainting(aOther.mResponseTainting) , mCacheMode(aOther.mCacheMode) + , mRedirectMode(aOther.mRedirectMode) , mAuthenticationFlag(aOther.mAuthenticationFlag) , mForceOriginHeader(aOther.mForceOriginHeader) , mPreserveContentCodings(aOther.mPreserveContentCodings) diff --git a/dom/fetch/InternalRequest.h b/dom/fetch/InternalRequest.h index c50af67c851..77258ae85dc 100644 --- a/dom/fetch/InternalRequest.h +++ b/dom/fetch/InternalRequest.h @@ -92,6 +92,7 @@ public: RESPONSETAINT_BASIC, RESPONSETAINT_CORS, RESPONSETAINT_OPAQUE, + RESPONSETAINT_OPAQUEREDIRECT, }; explicit InternalRequest() @@ -102,6 +103,7 @@ public: , mCredentialsMode(RequestCredentials::Omit) , mResponseTainting(RESPONSETAINT_BASIC) , mCacheMode(RequestCache::Default) + , mRedirectMode(RequestRedirect::Follow) , mAuthenticationFlag(false) , mForceOriginHeader(false) , mPreserveContentCodings(false) @@ -264,6 +266,18 @@ public: mCacheMode = aCacheMode; } + RequestRedirect + GetRedirectMode() const + { + return mRedirectMode; + } + + void + SetRedirectMode(RequestRedirect aRedirectMode) + { + mRedirectMode = aRedirectMode; + } + nsContentPolicyType ContentPolicyType() const { @@ -390,6 +404,7 @@ private: RequestCredentials mCredentialsMode; ResponseTainting mResponseTainting; RequestCache mCacheMode; + RequestRedirect mRedirectMode; bool mAuthenticationFlag; bool mForceOriginHeader; diff --git a/dom/fetch/InternalResponse.cpp b/dom/fetch/InternalResponse.cpp index 6a1f3f6dd40..4a598ea02c3 100644 --- a/dom/fetch/InternalResponse.cpp +++ b/dom/fetch/InternalResponse.cpp @@ -125,7 +125,6 @@ InternalResponse::OpaqueResponse() nsRefPtr response = new InternalResponse(0, EmptyCString()); response->mType = ResponseType::Opaque; response->mTerminationReason = mTerminationReason; - response->mURL = mURL; response->mChannelInfo = mChannelInfo; if (mPrincipalInfo) { response->mPrincipalInfo = MakeUnique(*mPrincipalInfo); @@ -134,6 +133,16 @@ InternalResponse::OpaqueResponse() return response.forget(); } +already_AddRefed +InternalResponse::OpaqueRedirectResponse() +{ + MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueRedirectResponse a already wrapped response"); + nsRefPtr response = OpaqueResponse(); + response->mType = ResponseType::Opaqueredirect; + response->mURL = mURL; + return response.forget(); +} + already_AddRefed InternalResponse::CreateIncompleteCopy() { diff --git a/dom/fetch/InternalResponse.h b/dom/fetch/InternalResponse.h index 826272bc1f8..eb1a352ae33 100644 --- a/dom/fetch/InternalResponse.h +++ b/dom/fetch/InternalResponse.h @@ -48,6 +48,9 @@ public: already_AddRefed OpaqueResponse(); + already_AddRefed + OpaqueRedirectResponse(); + already_AddRefed BasicResponse(); @@ -62,6 +65,7 @@ public: MOZ_ASSERT_IF(mType == ResponseType::Basic, mWrappedResponse); MOZ_ASSERT_IF(mType == ResponseType::Cors, mWrappedResponse); MOZ_ASSERT_IF(mType == ResponseType::Opaque, mWrappedResponse); + MOZ_ASSERT_IF(mType == ResponseType::Opaqueredirect, mWrappedResponse); return mType; } @@ -91,12 +95,32 @@ public: return mStatus; } + uint16_t + GetUnfilteredStatus() const + { + if (mWrappedResponse) { + return mWrappedResponse->GetStatus(); + } + + return GetStatus(); + } + const nsCString& GetStatusText() const { return mStatusText; } + const nsCString& + GetUnfilteredStatusText() const + { + if (mWrappedResponse) { + return mWrappedResponse->GetStatusText(); + } + + return GetStatusText(); + } + InternalHeaders* Headers() { @@ -114,7 +138,7 @@ public: } void - GetInternalBody(nsIInputStream** aStream) + GetUnfilteredBody(nsIInputStream** aStream) { if (mWrappedResponse) { MOZ_ASSERT(!mBody); @@ -127,12 +151,13 @@ public: void GetBody(nsIInputStream** aStream) { - if (Type() == ResponseType::Opaque) { + if (Type() == ResponseType::Opaque || + Type() == ResponseType::Opaqueredirect) { *aStream = nullptr; return; } - return GetInternalBody(aStream); + return GetUnfilteredBody(aStream); } void diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 0a3a21602c2..9f0674676bc 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -291,6 +291,10 @@ Request::Constructor(const GlobalObject& aGlobal, request->SetCacheMode(cache); } + if (aInit.mRedirect.WasPassed()) { + request->SetRedirectMode(aInit.mRedirect.Value()); + } + // Request constructor step 14. if (aInit.mMethod.WasPassed()) { nsAutoCString method(aInit.mMethod.Value()); diff --git a/dom/fetch/Request.h b/dom/fetch/Request.h index da926545e06..60fb06f92b2 100644 --- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -76,6 +76,12 @@ public: return mRequest->GetCacheMode(); } + RequestRedirect + Redirect() const + { + return mRequest->GetRedirectMode(); + } + RequestContext Context() const { From 3f82333cf8d02197edbe2fa3959184d72f314a98 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 37/85] Bug 1184607 P3 Add a RedirectMode flag to nsIHttpChannelInternal. r=nsm * * * Bug 1184607 P3 interdiff 001 http channel nits --- netwerk/protocol/http/HttpBaseChannel.cpp | 34 ++++++++++++++----- netwerk/protocol/http/HttpBaseChannel.h | 3 ++ .../protocol/http/nsIHttpChannelInternal.idl | 12 ++++++- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index cee55bc0184..2d4a0ac5637 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -88,6 +88,7 @@ HttpBaseChannel::HttpBaseChannel() , mForcePending(false) , mCorsIncludeCredentials(false) , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS) + , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) , mOnStartRequestCalled(false) { LOG(("Creating HttpBaseChannel @%x\n", this)); @@ -2002,6 +2003,20 @@ HttpBaseChannel::SetCorsMode(uint32_t aMode) return NS_OK; } +NS_IMETHODIMP +HttpBaseChannel::GetRedirectMode(uint32_t* aMode) +{ + *aMode = mRedirectMode; + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetRedirectMode(uint32_t aMode) +{ + mRedirectMode = aMode; + return NS_OK; +} + //----------------------------------------------------------------------------- // HttpBaseChannel::nsISupportsPriority //----------------------------------------------------------------------------- @@ -2277,14 +2292,6 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, } } - // Preserve any skip-serviceworker-flag if possible. - if (mForceNoIntercept) { - nsCOMPtr httpChan = do_QueryInterface(newChannel); - if (httpChan) { - httpChan->ForceNoIntercept(); - } - } - // Propagate our loadinfo if needed. newChannel->SetLoadInfo(mLoadInfo); @@ -2384,6 +2391,17 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, "[this=%p] transferring chain of redirect cache-keys", this)); httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys.forget()); } + + // Preserve any skip-serviceworker-flag. + if (mForceNoIntercept) { + httpInternal->ForceNoIntercept(); + } + + // Preserve CORS mode flag. + httpInternal->SetCorsMode(mCorsMode); + + // Preserve Redirect mode flag. + httpInternal->SetRedirectMode(mRedirectMode); } // transfer application cache information diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index f5d52d8173a..e3e3b9d6571 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -195,6 +195,8 @@ public: NS_IMETHOD SetCorsIncludeCredentials(bool aInclude) override; NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override; NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override; + NS_IMETHOD GetRedirectMode(uint32_t* aRedirectMode) override; + NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override; NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override; NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override; @@ -429,6 +431,7 @@ protected: bool mCorsIncludeCredentials; uint32_t mCorsMode; + uint32_t mRedirectMode; // This parameter is used to ensure that we do not call OnStartRequest more // than once. diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl index da3dca3f0bb..0af6d525cde 100644 --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl @@ -38,7 +38,7 @@ interface nsIHttpUpgradeListener : nsISupports * using any feature exposed by this interface, be aware that this interface * will change and you will be broken. You have been warned. */ -[scriptable, uuid(c4fab96f-0b10-4b14-b45b-517fc3f36842)] +[scriptable, uuid(e2eebad8-e51f-473a-bbb7-8e2829376625)] interface nsIHttpChannelInternal : nsISupports { @@ -239,6 +239,16 @@ interface nsIHttpChannelInternal : nsISupports */ attribute unsigned long corsMode; + const unsigned long REDIRECT_MODE_FOLLOW = 0; + const unsigned long REDIRECT_MODE_ERROR = 1; + const unsigned long REDIRECT_MODE_MANUAL = 2; + /** + * Set to indicate Request.redirect mode exposed during ServiceWorker + * interception. No policy enforcement is performed by the channel for this + * value. + */ + attribute unsigned long redirectMode; + /** * The URI of the top-level window that's associated with this channel. */ From e965d22802356d3b86b78dac8efd8bf41a018bd1 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 38/85] Bug 1184607 P4 Handle the RequestRedirect mode during service worker interception. r=nsm * * * Bug 1184607 P4 interdiff 001 fix manual redirect assertion for navigations r=nsm * * * Bug 1184607 P4 interdiff 002 dom/worker nits --- docshell/base/nsDocShell.cpp | 1 + dom/base/nsContentUtils.cpp | 2 ++ dom/locales/en-US/chrome/dom/dom.properties | 4 ++- dom/workers/ServiceWorkerEvents.cpp | 29 +++++++++++++------ dom/workers/ServiceWorkerManager.cpp | 32 +++++++++++++++++++-- xpcom/base/ErrorList.h | 2 ++ 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 4857ba79fbf..0c0e6cd3147 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4970,6 +4970,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, case NS_ERROR_INTERCEPTED_ERROR_RESPONSE: case NS_ERROR_INTERCEPTED_USED_RESPONSE: case NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION: + case NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION: // ServiceWorker intercepted request, but something went wrong. nsContentUtils::MaybeReportInterceptionErrorToConsole(GetDocument(), aError); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 0b0e1e66d9b..a71c84ff93e 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3458,6 +3458,8 @@ nsContentUtils::MaybeReportInterceptionErrorToConsole(nsIDocument* aDocument, messageName = "InterceptedUsedResponse"; } else if (aError == NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION) { messageName = "ClientRequestOpaqueInterception"; + } else if (aError == NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION) { + messageName = "BadOpaqueRedirectInterception"; } if (messageName) { diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 2377e8c0f7b..66ae0e3c276 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -173,5 +173,7 @@ BadOpaqueInterceptionRequestMode=A ServiceWorker passed an opaque Response to Fe InterceptedErrorResponse=A ServiceWorker passed an Error Response to FetchEvent.respondWith(). This typically means the ServiceWorker performed an invalid fetch() call. # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", or "Response.clone()". InterceptedUsedResponse=A ServiceWorker passed a used Response to FetchEvent.respondWith(). The body of a Response may only be read once. Use Response.clone() to access the body multiple times. -# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", "FetchEvent.request", or "Worker". +# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaque", "Response", "FetchEvent.respondWith()", "FetchEvent.request", or "Worker". ClientRequestOpaqueInterception=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while FetchEvent.request was a client request. A client request is generally a browser navigation or top-level Worker script. +# LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent.request". +BadOpaqueRedirectInterception=A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while FetchEvent.request was not a navigation request. diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 10561108801..72a57162da1 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -130,7 +130,8 @@ public: return rv; } - mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText()); + mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(), + mInternalResponse->GetUnfilteredStatusText()); nsAutoTArray entries; mInternalResponse->UnfilteredHeaders()->GetEntries(entries); @@ -148,18 +149,21 @@ class RespondWithHandler final : public PromiseNativeHandler { nsMainThreadPtrHandle mInterceptedChannel; nsMainThreadPtrHandle mServiceWorker; - RequestMode mRequestMode; - bool mIsClientRequest; + const RequestMode mRequestMode; + const bool mIsClientRequest; + const bool mIsNavigationRequest; public: NS_DECL_ISUPPORTS RespondWithHandler(nsMainThreadPtrHandle& aChannel, nsMainThreadPtrHandle& aServiceWorker, - RequestMode aRequestMode, bool aIsClientRequest) + RequestMode aRequestMode, bool aIsClientRequest, + bool aIsNavigationRequest) : mInterceptedChannel(aChannel) , mServiceWorker(aServiceWorker) , mRequestMode(aRequestMode) , mIsClientRequest(aIsClientRequest) + , mIsNavigationRequest(aIsNavigationRequest) { } @@ -264,12 +268,14 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } - // Section 4.2, step 2.2: + // Section "HTTP Fetch", step 2.2: // If one of the following conditions is true, return a network error: // * response's type is "error". // * request's mode is not "no-cors" and response's type is "opaque". // * request is a client request and response's type is neither "basic" // nor "default". + // * request is not a navigation request and response's type is + // "opaqueredirect". if (response->Type() == ResponseType::Error) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_ERROR_RESPONSE); @@ -281,12 +287,19 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu return; } + // TODO: remove this case as its no longer in the spec (bug 1184967) if (mIsClientRequest && response->Type() != ResponseType::Basic && - response->Type() != ResponseType::Default) { + response->Type() != ResponseType::Default && + response->Type() != ResponseType::Opaqueredirect) { autoCancel.SetCancelStatus(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION); return; } + if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) { + autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION); + return; + } + if (NS_WARN_IF(response->BodyUsed())) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_USED_RESPONSE); return; @@ -300,7 +313,7 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle aValu nsAutoPtr closure( new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo())); nsCOMPtr body; - ir->GetInternalBody(getter_AddRefs(body)); + ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); @@ -359,7 +372,7 @@ FetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv) mWaitToRespond = true; nsRefPtr handler = new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode(), - ir->IsClientRequest()); + ir->IsClientRequest(), ir->IsNavigationRequest()); aArg.AppendNativeHandler(handler); } diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 77f4b5326dc..1ed17dfe20c 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -94,6 +94,15 @@ static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast(Re static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast(RequestMode::Cors_with_forced_preflight), "RequestMode enumeration value should match Necko CORS mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast(RequestRedirect::Follow), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast(RequestRedirect::Error), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast(RequestRedirect::Manual), + "RequestRedirect enumeration value should make Necko Redirect mode value."); +static_assert(3 == static_cast(RequestRedirect::EndGuard_), + "RequestRedirect enumeration value should make Necko Redirect mode value."); + static StaticRefPtr gInstance; // Tracks the "dom.disable_open_click_delay" preference. Modified on main @@ -3616,7 +3625,9 @@ class FetchEventRunnable : public WorkerRunnable nsCString mSpec; nsCString mMethod; bool mIsReload; + DebugOnly mIsHttpChannel; RequestMode mRequestMode; + RequestRedirect mRequestRedirect; RequestCredentials mRequestCredentials; nsContentPolicyType mContentPolicyType; nsCOMPtr mUploadStream; @@ -3632,7 +3643,9 @@ public: , mServiceWorker(aServiceWorker) , mClientInfo(aClientInfo) , mIsReload(aIsReload) + , mIsHttpChannel(false) , mRequestMode(RequestMode::No_cors) + , mRequestRedirect(RequestRedirect::Follow) // By default we set it to same-origin since normal HTTP fetches always // send credentials to same-origin websites unless explicitly forbidden. , mRequestCredentials(RequestCredentials::Same_origin) @@ -3688,15 +3701,17 @@ public: nsCOMPtr httpChannel = do_QueryInterface(channel); if (httpChannel) { + mIsHttpChannel = true; + rv = httpChannel->GetRequestMethod(mMethod); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr internalChannel = do_QueryInterface(httpChannel); NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); - uint32_t mode; - internalChannel->GetCorsMode(&mode); - switch (mode) { + uint32_t corsMode; + internalChannel->GetCorsMode(&corsMode); + switch (corsMode) { case nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN: mRequestMode = RequestMode::Same_origin; break; @@ -3711,6 +3726,11 @@ public: MOZ_CRASH("Unexpected CORS mode"); } + // This is safe due to static_asserts at top of file. + uint32_t redirectMode; + internalChannel->GetRedirectMode(&redirectMode); + mRequestRedirect = static_cast(redirectMode); + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { mRequestCredentials = RequestCredentials::Omit; } else { @@ -3803,6 +3823,7 @@ private: reqInit.mHeaders.Value().SetAsHeaders() = headers; reqInit.mMode.Construct(mRequestMode); + reqInit.mRedirect.Construct(mRequestRedirect); reqInit.mCredentials.Construct(mRequestCredentials); ErrorResult result; @@ -3821,6 +3842,11 @@ private: request->SetContentPolicyType(mContentPolicyType); + // TODO: remove conditional on http here once app protocol support is + // removed from service worker interception + MOZ_ASSERT_IF(mIsHttpChannel && internalReq->IsNavigationRequest(), + request->Redirect() == RequestRedirect::Manual); + RootedDictionary init(aCx); init.mRequest.Construct(); init.mRequest.Value() = request; diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 0ed10aece55..de539932737 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -333,6 +333,8 @@ ERROR(NS_ERROR_INTERCEPTED_USED_RESPONSE, FAILURE(104)), /* Service worker intercepted a client request with an opaque response */ ERROR(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION, FAILURE(105)), + /* Service worker intercepted a non-navigation with an opaque redirect */ + ERROR(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION, FAILURE(106)), #undef MODULE From 2e7f710fed630772506f1020686394eef312c657 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 39/85] Bug 1184607 P5 Set RequestRedirect to "manual" for navigations. r=nsm * * * Bug 1184607 P5 interdiff 001 don't set manual redirect mode for html imports --- docshell/base/nsDocShell.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 0c0e6cd3147..a02e90a7876 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -10564,6 +10564,8 @@ nsDocShell::DoURILoad(nsIURI* aURI, } else { httpChannelInternal->SetDocumentURI(aReferrerURI); } + httpChannelInternal->SetRedirectMode( + nsIHttpChannelInternal::REDIRECT_MODE_MANUAL); } nsCOMPtr props(do_QueryInterface(channel)); From 6c291781856e9055b5afc8d0959d9a94bdde4525 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 40/85] Bug 1184607 P6 Set RequestRedirect and fix various redirect bugs in FetchDriver. r=nsm * * * Bug 1184607 P6 interdiff 001 --- dom/fetch/FetchDriver.cpp | 216 ++++++++++++++++++++++++++++---------- dom/fetch/FetchDriver.h | 23 ++++ 2 files changed, 184 insertions(+), 55 deletions(-) diff --git a/dom/fetch/FetchDriver.cpp b/dom/fetch/FetchDriver.cpp index 40b716abb4d..46ae6d3de95 100644 --- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -31,6 +31,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/workers/Workers.h" +#include "mozilla/unused.h" #include "Fetch.h" #include "InternalRequest.h" @@ -49,6 +50,7 @@ FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal, , mLoadGroup(aLoadGroup) , mRequest(aRequest) , mFetchRecursionCount(0) + , mCORSFlagEverSet(false) , mResponseAvailableCalled(false) { } @@ -94,8 +96,8 @@ FetchDriver::Fetch(bool aCORSFlag) MOZ_CRASH("Synchronous fetch not supported"); } -nsresult -FetchDriver::ContinueFetch(bool aCORSFlag) +FetchDriver::MainFetchOp +FetchDriver::SetTaintingAndGetNextOp(bool aCORSFlag) { workers::AssertIsOnMainThread(); @@ -105,7 +107,7 @@ FetchDriver::ContinueFetch(bool aCORSFlag) nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { - return FailWithNetworkError(); + return MainFetchOp(NETWORK_ERROR); } // CSP/mixed content checks. @@ -123,51 +125,91 @@ FetchDriver::ContinueFetch(bool aCORSFlag) nsContentUtils::GetSecurityManager()); if (NS_WARN_IF(NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))) { // Disallowed by content policy. - return FailWithNetworkError(); + return MainFetchOp(NETWORK_ERROR); } - // Begin Step 4 of the Fetch algorithm + // Begin Step 8 of the Main Fetch algorithm // https://fetch.spec.whatwg.org/#fetching nsAutoCString scheme; rv = requestURI->GetScheme(scheme); if (NS_WARN_IF(NS_FAILED(rv))) { - return FailWithNetworkError(); + return MainFetchOp(NETWORK_ERROR); } - rv = mPrincipal->CheckMayLoad(requestURI, false /* report */, false /* allowIfInheritsPrincipal */); + // request's current url's origin is request's origin and the CORS flag is unset + // request's current url's scheme is "data" and request's same-origin data-URL flag is set + // request's current url's scheme is "about" + rv = mPrincipal->CheckMayLoad(requestURI, false /* report */, + false /* allowIfInheritsPrincipal */); if ((!aCORSFlag && NS_SUCCEEDED(rv)) || (scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) || scheme.EqualsLiteral("about")) { - return BasicFetch(); + return MainFetchOp(BASIC_FETCH); } + // request's mode is "same-origin" if (mRequest->Mode() == RequestMode::Same_origin) { - return FailWithNetworkError(); + return MainFetchOp(NETWORK_ERROR); } + // request's mode is "no-cors" if (mRequest->Mode() == RequestMode::No_cors) { mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE); - return BasicFetch(); + return MainFetchOp(BASIC_FETCH); } + // request's current url's scheme is not one of "http" and "https" if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) { + return MainFetchOp(NETWORK_ERROR); + } + + // request's mode is "cors-with-forced-preflight" + // request's unsafe-request flag is set and either request's method is not + // a simple method or a header in request's header list is not a simple header + if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight || + (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || + !mRequest->Headers()->HasOnlySimpleHeaders()))) { + mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS); + mRequest->SetRedirectMode(RequestRedirect::Error); + + // Note, the following text from Main Fetch step 8 is handled in + // nsCORSListenerProxy when CheckRequestApproved() fails: + // + // The result of performing an HTTP fetch using request with the CORS + // flag and CORS-preflight flag set. If the result is a network error, + // clear cache entries using request. + + return MainFetchOp(HTTP_FETCH, true /* cors */, true /* preflight */); + } + + // Otherwise + mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS); + return MainFetchOp(HTTP_FETCH, true /* cors */, false /* preflight */); +} + +nsresult +FetchDriver::ContinueFetch(bool aCORSFlag) +{ + workers::AssertIsOnMainThread(); + + MainFetchOp nextOp = SetTaintingAndGetNextOp(aCORSFlag); + + if (nextOp.mType == NETWORK_ERROR) { return FailWithNetworkError(); } - bool corsPreflight = false; - if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight || - (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) { - corsPreflight = true; + if (nextOp.mType == BASIC_FETCH) { + return BasicFetch(); } - // The Request constructor should ensure that no-cors requests have simple - // method and headers, so we should never attempt to preflight for such - // Requests. - MOZ_ASSERT_IF(mRequest->Mode() == RequestMode::No_cors, !corsPreflight); - mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS); - return HttpFetch(true /* aCORSFlag */, corsPreflight); -} + if (nextOp.mType == HTTP_FETCH) { + return HttpFetch(nextOp.mCORSFlag, nextOp.mCORSPreflightFlag); + } + + MOZ_ASSERT_UNREACHABLE("Unexpected main fetch operation!"); + return FailWithNetworkError(); + } nsresult FetchDriver::BasicFetch() @@ -331,6 +373,11 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica mResponse = nullptr; nsresult rv; + // We need to track the CORS flag through redirects. Since there is no way + // for us to go from CORS mode to non-CORS mode, we just need to remember + // if it has ever been set. + mCORSFlagEverSet = mCORSFlagEverSet || aCORSFlag; + nsCOMPtr ios = do_GetIOService(&rv); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); @@ -487,6 +534,13 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica // Auth may require prompting, we don't support it yet. // The next patch in this same bug prevents this from aborting the request. // Credentials checks for CORS are handled by nsCORSListenerProxy, + + nsCOMPtr internalChan = do_QueryInterface(httpChan); + + // Conversion between enumerations is safe due to static asserts in + // dom/workers/ServiceWorkerManager.cpp + internalChan->SetCorsMode(static_cast(mRequest->Mode())); + internalChan->SetRedirectMode(static_cast(mRequest->GetRedirectMode())); } // Step 5. Proxy authentication will be handled by Necko. @@ -534,10 +588,10 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica nsCOMPtr listener = this; - // Unless the cors mode is explicitly no-cors, we set up a cors proxy even in - // the same-origin case, since the proxy does not enforce cors header checks - // in the same-origin case. - if (mRequest->Mode() != RequestMode::No_cors) { + // Only use nsCORSListenerProxy if we are in CORS mode. Otherwise it + // will overwrite the CorsMode flag unconditionally to "cors" or + // "cors-with-forced-preflight". + if (mRequest->Mode() == RequestMode::Cors) { // Set up a CORS proxy that will handle the various requirements of the CORS // protocol. It handles the preflight cache and CORS response headers. // If the request is allowed, it will start our original request @@ -616,6 +670,9 @@ FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aF case InternalRequest::RESPONSETAINT_OPAQUE: filteredResponse = aResponse->OpaqueResponse(); break; + case InternalRequest::RESPONSETAINT_OPAQUEREDIRECT: + filteredResponse = aResponse->OpaqueRedirectResponse(); + break; default: MOZ_CRASH("Unexpected case"); } @@ -696,8 +753,11 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { workers::AssertIsOnMainThread(); - MOZ_ASSERT(!mPipeOutputStream); - MOZ_ASSERT(mObserver); + + // Note, this can be called multiple times if we are doing an opaqueredirect. + // In that case we will get a simulated OnStartRequest() and then the real + // channel will call in with an errored OnStartRequest(). + nsresult rv; aRequest->GetStatus(&rv); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -705,6 +765,10 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest, return rv; } + // We should only get to the following code once. + MOZ_ASSERT(!mPipeOutputStream); + MOZ_ASSERT(mObserver); + nsRefPtr response; nsCOMPtr httpChannel = do_QueryInterface(aRequest); if (httpChannel) { @@ -839,33 +903,59 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsresult rv; - // Section 4.2, Step 4.6-4.7, enforcing a redirect count is done by Necko. - // The pref used is "network.http.redirection-limit" which is set to 20 by - // default. - // - // Step 4.8. We only unset this for spec compatibility. Any actions we take - // on mRequest here do not affect what the channel does. + // HTTP Fetch step 5, "redirect status", step 1 + if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) { + aOldChannel->Cancel(NS_BINDING_FAILED); + return NS_BINDING_FAILED; + } + + // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically + // handled by necko before calling AsyncOnChannelRedirect() with the new + // nsIChannel. + + // HTTP Fetch step 5, "redirect status", steps 7 and 8 enforcing a redirect + // count are done by Necko. The pref used is "network.http.redirection-limit" + // which is set to 20 by default. + + // HTTP Fetch Step 9, "redirect status". We only unset this for spec + // compatibility. Any actions we take on mRequest here do not affect what the + //channel does. mRequest->UnsetSameOriginDataURL(); - // - // Requests that require preflight are not permitted to redirect. - // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual - // redirect flag to decide whether to execute step 4.10 or not. We do not - // represent it in our implementation. - // The only thing we do is to check if the request requires a preflight (part - // of step 4.9), in which case we abort. This part cannot be done by - // nsCORSListenerProxy since it does not have access to mRequest. - // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all - // the other steps are handled by nsCORSListenerProxy. - if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { - rv = DoesNotRequirePreflight(aNewChannel); - if (NS_FAILED(rv)) { - NS_WARNING("FetchDriver::OnChannelRedirect: " - "DoesNotRequirePreflight returned failure"); - return rv; - } + // HTTP Fetch step 5, "redirect status", step 10 requires us to halt the + // redirect, but successfully return an opaqueredirect Response to the + // initiating Fetch. + if (mRequest->GetRedirectMode() == RequestRedirect::Manual) { + // Ideally we would simply not cancel the old channel and allow it to + // be processed as normal. Unfortunately this is quite fragile and + // other redirect handlers can easily break it for certain use cases. + // + // For example, nsCORSListenerProxy cancels vetoed redirect channels. + // The HTTP cache will also error on vetoed redirects when the + // redirect has been previously cached. + // + // Therefore simulate the completion of the channel to produce the + // opaqueredirect Response and then cancel the original channel. This + // will result in OnStartRequest() getting called twice, but the second + // time will be with an error response (from the Cancel) which will + // be ignored. + mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUEREDIRECT); + unused << OnStartRequest(aOldChannel, nullptr); + unused << OnStopRequest(aOldChannel, nullptr, NS_OK); + + aOldChannel->Cancel(NS_BINDING_FAILED); + + return NS_BINDING_FAILED; } + // The following steps are from HTTP Fetch step 5, "redirect status", step 11 + // which requires the RequestRedirect to be "follow". + MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow); + + // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting + // to a URL with credentials in CORS mode. This is implemented in + // nsCORSListenerProxy. + mRedirectCallback = aCallback; mOldRedirectChannel = aOldChannel; mNewRedirectChannel = aNewChannel; @@ -958,12 +1048,12 @@ FetchDriver::GetInterface(const nsIID& aIID, void **aResult) NS_IMETHODIMP FetchDriver::OnRedirectVerifyCallback(nsresult aResult) { - // On a successful redirect we perform the following substeps of Section 4.2, - // step 4.10. + // On a successful redirect we perform the following substeps of HTTP Fetch, + // step 5, "redirect status", step 11. if (NS_SUCCEEDED(aResult)) { - // Step 4.10.3 "Set request's url to locationURL." so that when we set the - // Response's URL from the Request's URL in Section 4, step 6, we get the - // final value. + // Step 11.5 "Append locationURL to request's url list." so that when we set the + // Response's URL from the Request's URL in Main Fetch, step 15, we get the + // final value. Note, we still use a single URL value instead of a list. nsCOMPtr newURI; nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI)); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -980,6 +1070,22 @@ FetchDriver::OnRedirectVerifyCallback(nsresult aResult) mOldRedirectChannel->Cancel(aResult); } + // Implement Main Fetch step 8 again on redirect. + MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet); + + if (nextOp.mType == NETWORK_ERROR) { + // Cancel the channel if Main Fetch blocks the redirect from continuing. + aResult = NS_ERROR_DOM_BAD_URI; + mOldRedirectChannel->Cancel(aResult); + } else { + // Otherwise, we rely on necko and the CORS proxy to do the right thing + // as the redirect is followed. In general this means basic or http + // fetch. If we've ever been CORS, we need to stay CORS. + MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH); + MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH); + MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag); + } + mOldRedirectChannel = nullptr; mNewRedirectChannel = nullptr; mRedirectCallback->OnRedirectVerifyCallback(aResult); diff --git a/dom/fetch/FetchDriver.h b/dom/fetch/FetchDriver.h index 1c95bbd10e7..96b8f848f20 100644 --- a/dom/fetch/FetchDriver.h +++ b/dom/fetch/FetchDriver.h @@ -77,6 +77,7 @@ private: nsCOMPtr mNewRedirectChannel; nsCOMPtr mDocument; uint32_t mFetchRecursionCount; + bool mCORSFlagEverSet; DebugOnly mResponseAvailableCalled; @@ -85,7 +86,29 @@ private: FetchDriver& operator=(const FetchDriver&) = delete; ~FetchDriver(); + enum MainFetchOpType + { + NETWORK_ERROR, + BASIC_FETCH, + HTTP_FETCH, + NUM_MAIN_FETCH_OPS + }; + + struct MainFetchOp + { + explicit MainFetchOp(MainFetchOpType aType, bool aCORSFlag = false, + bool aCORSPreflightFlag = false) + : mType(aType), mCORSFlag(aCORSFlag), + mCORSPreflightFlag(aCORSPreflightFlag) + { } + + MainFetchOpType mType; + bool mCORSFlag; + bool mCORSPreflightFlag; + }; + nsresult Fetch(bool aCORSFlag); + MainFetchOp SetTaintingAndGetNextOp(bool aCORSFlag); nsresult ContinueFetch(bool aCORSFlag); nsresult BasicFetch(); nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false); From 211ea4ed3f7cd52aef38bb4b02848ce5b10b92dc Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:29 -0700 Subject: [PATCH 41/85] Bug 1184607 P7.1 Move Cache schema SQL into separate constants. r=ehsan --- dom/cache/DBSchema.cpp | 236 ++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 108 deletions(-) diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 3270c1cb7f1..fdaabb35b5f 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -36,6 +36,125 @@ const int32_t kMaxWipeSchemaVersion = 15; namespace { const int32_t kLatestSchemaVersion = 15; + +// --------- +// The following constants define the SQL schema. These are defined in the +// same order the SQL should be executed in CreateSchema(). They are broken +// out as constants for convenient use in validation and migration. +// --------- + +// The caches table is the single source of truth about what Cache +// objects exist for the origin. The contents of the Cache are stored +// in the entries table that references back to caches. +// +// The caches table is also referenced from storage. Rows in storage +// represent named Cache objects. There are cases, however, where +// a Cache can still exist, but not be in a named Storage. For example, +// when content is still using the Cache after CacheStorage::Delete() +// has been run. +// +// For now, the caches table mainly exists for data integrity with +// foreign keys, but could be expanded to contain additional cache object +// information. +// +// AUTOINCREMENT is necessary to prevent CacheId values from being reused. +const char* const kTableCaches = + "CREATE TABLE caches (" + "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT " + ")"; + +// Security blobs are quite large and duplicated for every Response from +// the same https origin. This table is used to de-duplicate this data. +const char* const kTableSecurityInfo = + "CREATE TABLE security_info (" + "id INTEGER NOT NULL PRIMARY KEY, " + "hash BLOB NOT NULL, " // first 8-bytes of the sha1 hash of data column + "data BLOB NOT NULL, " // full security info data, usually a few KB + "refcount INTEGER NOT NULL" + ")"; + +// Index the smaller hash value instead of the large security data blob. +const char* const kIndexSecurityInfoHash = + "CREATE INDEX security_info_hash_index ON security_info (hash)"; + +const char* const kTableEntries = + "CREATE TABLE entries (" + "id INTEGER NOT NULL PRIMARY KEY, " + "request_method TEXT NOT NULL, " + "request_url_no_query TEXT NOT NULL, " + "request_url_no_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash + "request_url_query TEXT NOT NULL, " + "request_url_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash + "request_referrer TEXT NOT NULL, " + "request_headers_guard INTEGER NOT NULL, " + "request_mode INTEGER NOT NULL, " + "request_credentials INTEGER NOT NULL, " + "request_contentpolicytype INTEGER NOT NULL, " + "request_cache INTEGER NOT NULL, " + "request_body_id TEXT NULL, " + "response_type INTEGER NOT NULL, " + "response_url TEXT NOT NULL, " + "response_status INTEGER NOT NULL, " + "response_status_text TEXT NOT NULL, " + "response_headers_guard INTEGER NOT NULL, " + "response_body_id TEXT NULL, " + "response_security_info_id INTEGER NULL REFERENCES security_info(id), " + "response_principal_info TEXT NOT NULL, " + "response_redirected INTEGER NOT NULL, " + // Note that response_redirected_url is either going to be empty, or + // it's going to be a URL different than response_url. + "response_redirected_url TEXT NOT NULL, " + "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE" + ")"; + +// Create an index to support the QueryCache() matching algorithm. This +// needs to quickly find entries in a given Cache that match the request +// URL. The url query is separated in order to support the ignoreSearch +// option. Finally, we index hashes of the URL values instead of the +// actual strings to avoid excessive disk bloat. The index will duplicate +// the contents of the columsn in the index. The hash index will prune +// the vast majority of values from the query result so that normal +// scanning only has to be done on a few values to find an exact URL match. +const char* const kIndexEntriesRequest = + "CREATE INDEX entries_request_match_index " + "ON entries (cache_id, request_url_no_query_hash, " + "request_url_query_hash)"; + +const char* const kTableRequestHeaders = + "CREATE TABLE request_headers (" + "name TEXT NOT NULL, " + "value TEXT NOT NULL, " + "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE" + ")"; + +const char* const kTableResponseHeaders = + "CREATE TABLE response_headers (" + "name TEXT NOT NULL, " + "value TEXT NOT NULL, " + "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE" + ")"; + +// We need an index on response_headers, but not on request_headers, +// because we quickly need to determine if a VARY header is present. +const char* const kIndexResponseHeadersName = + "CREATE INDEX response_headers_name_index " + "ON response_headers (name)"; + +// NOTE: key allows NULL below since that is how "" is represented +// in a BLOB column. We use BLOB to avoid encoding issues +// with storing DOMStrings. +const char* const kTableStorage = + "CREATE TABLE storage (" + "namespace INTEGER NOT NULL, " + "key BLOB NULL, " + "cache_id INTEGER NOT NULL REFERENCES caches(id), " + "PRIMARY KEY(namespace, key) " + ")"; + +// --------- +// End schema definition +// --------- + const int32_t kMaxEntriesPerStatement = 255; const uint32_t kPageSize = 4 * 1024; @@ -224,130 +343,31 @@ CreateSchema(mozIStorageConnection* aConn) } if (!schemaVersion) { - // The caches table is the single source of truth about what Cache - // objects exist for the origin. The contents of the Cache are stored - // in the entries table that references back to caches. - // - // The caches table is also referenced from storage. Rows in storage - // represent named Cache objects. There are cases, however, where - // a Cache can still exist, but not be in a named Storage. For example, - // when content is still using the Cache after CacheStorage::Delete() - // has been run. - // - // For now, the caches table mainly exists for data integrity with - // foreign keys, but could be expanded to contain additional cache object - // information. - // - // AUTOINCREMENT is necessary to prevent CacheId values from being reused. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE caches (" - "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT " - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableCaches)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // Security blobs are quite large and duplicated for every Response from - // the same https origin. This table is used to de-duplicate this data. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE security_info (" - "id INTEGER NOT NULL PRIMARY KEY, " - "hash BLOB NOT NULL, " // first 8-bytes of the sha1 hash of data column - "data BLOB NOT NULL, " // full security info data, usually a few KB - "refcount INTEGER NOT NULL" - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableSecurityInfo)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // Index the smaller hash value instead of the large security data blob. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX security_info_hash_index ON security_info (hash);" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexSecurityInfoHash)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE entries (" - "id INTEGER NOT NULL PRIMARY KEY, " - "request_method TEXT NOT NULL, " - "request_url_no_query TEXT NOT NULL, " - "request_url_no_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash - "request_url_query TEXT NOT NULL, " - "request_url_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash - "request_referrer TEXT NOT NULL, " - "request_headers_guard INTEGER NOT NULL, " - "request_mode INTEGER NOT NULL, " - "request_credentials INTEGER NOT NULL, " - "request_contentpolicytype INTEGER NOT NULL, " - "request_cache INTEGER NOT NULL, " - "request_body_id TEXT NULL, " - "response_type INTEGER NOT NULL, " - "response_url TEXT NOT NULL, " - "response_status INTEGER NOT NULL, " - "response_status_text TEXT NOT NULL, " - "response_headers_guard INTEGER NOT NULL, " - "response_body_id TEXT NULL, " - "response_security_info_id INTEGER NULL REFERENCES security_info(id), " - "response_principal_info TEXT NOT NULL, " - "response_redirected INTEGER NOT NULL, " - // Note that response_redirected_url is either going to be empty, or - // it's going to be a URL different than response_url. - "response_redirected_url TEXT NOT NULL, " - "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE" - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableEntries)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // Create an index to support the QueryCache() matching algorithm. This - // needs to quickly find entries in a given Cache that match the request - // URL. The url query is separated in order to support the ignoreSearch - // option. Finally, we index hashes of the URL values instead of the - // actual strings to avoid excessive disk bloat. The index will duplicate - // the contents of the columsn in the index. The hash index will prune - // the vast majority of values from the query result so that normal - // scanning only has to be done on a few values to find an exact URL match. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX entries_request_match_index " - "ON entries (cache_id, request_url_no_query_hash, " - "request_url_query_hash);" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE request_headers (" - "name TEXT NOT NULL, " - "value TEXT NOT NULL, " - "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE" - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableRequestHeaders)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE response_headers (" - "name TEXT NOT NULL, " - "value TEXT NOT NULL, " - "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE" - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableResponseHeaders)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // We need an index on response_headers, but not on request_headers, - // because we quickly need to determine if a VARY header is present. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX response_headers_name_index " - "ON response_headers (name);" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexResponseHeadersName)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // NOTE: key allows NULL below since that is how "" is represented - // in a BLOB column. We use BLOB to avoid encoding issues - // with storing DOMStrings. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE storage (" - "namespace INTEGER NOT NULL, " - "key BLOB NULL, " - "cache_id INTEGER NOT NULL REFERENCES caches(id), " - "PRIMARY KEY(namespace, key) " - ");" - )); + rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableStorage)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = aConn->SetSchemaVersion(kLatestSchemaVersion); From ed5fbacacc90ee617ba9bfc387538bc6a64de1a6 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 42/85] Bug 1184607 P7.2 Validate Cache schema in debug builds. r=ehsan --- dom/cache/DBSchema.cpp | 122 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index fdaabb35b5f..7e4dd5ecb3e 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -325,6 +325,7 @@ static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn, mozIStorageStatement** aStateOut); static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut); +nsresult Validate(mozIStorageConnection* aConn); } // namespace nsresult @@ -338,7 +339,11 @@ CreateSchema(mozIStorageConnection* aConn) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (schemaVersion == kLatestSchemaVersion) { - // We already have the correct schema, so just get started. + // We already have the correct schema version. Validate it matches + // our expected schema and then proceed. + rv = Validate(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + return rv; } @@ -377,9 +382,8 @@ CreateSchema(mozIStorageConnection* aConn) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - if (schemaVersion != kLatestSchemaVersion) { - return NS_ERROR_FAILURE; - } + rv = Validate(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return rv; } @@ -2205,6 +2209,116 @@ IncrementalVacuum(mozIStorageConnection* aConn) return NS_OK; } +namespace { + +#ifdef DEBUG +struct Expect +{ + // Expect exact SQL + Expect(const char* aName, const char* aType, const char* aSql) + : mName(aName) + , mType(aType) + , mSql(aSql) + , mIgnoreSql(false) + { } + + // Ignore SQL + Expect(const char* aName, const char* aType) + : mName(aName) + , mType(aType) + , mIgnoreSql(true) + { } + + const nsCString mName; + const nsCString mType; + const nsCString mSql; + const bool mIgnoreSql; +}; +#endif + +nsresult +Validate(mozIStorageConnection* aConn) +{ + int32_t schemaVersion; + nsresult rv = aConn->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (NS_WARN_IF(schemaVersion != kLatestSchemaVersion)) { + return NS_ERROR_FAILURE; + } + +#ifdef DEBUG + // This is the schema we expect the database at the latest version to + // contain. Update this list if you add a new table or index. + Expect expect[] = { + Expect("caches", "table", kTableCaches), + Expect("sqlite_sequence", "table"), // auto-gen by sqlite + Expect("security_info", "table", kTableSecurityInfo), + Expect("security_info_hash_index", "index", kIndexSecurityInfoHash), + Expect("entries", "table", kTableEntries), + Expect("entries_request_match_index", "index", kIndexEntriesRequest), + Expect("request_headers", "table", kTableRequestHeaders), + Expect("response_headers", "table", kTableResponseHeaders), + Expect("response_headers_name_index", "index", kIndexResponseHeadersName), + Expect("storage", "table", kTableStorage), + Expect("sqlite_autoindex_storage_1", "index"), // auto-gen by sqlite + }; + const uint32_t expectLength = sizeof(expect) / sizeof(Expect); + + // Read the schema from the sqlite_master table and compare. + nsCOMPtr state; + rv = aConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, type, sql FROM sqlite_master;" + ), getter_AddRefs(state)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + bool hasMoreData = false; + while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) { + nsAutoCString name; + rv = state->GetUTF8String(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsAutoCString type; + rv = state->GetUTF8String(1, type); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsAutoCString sql; + rv = state->GetUTF8String(2, sql); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + bool foundMatch = false; + for (uint32_t i = 0; i < expectLength; ++i) { + if (name == expect[i].mName) { + if (type != expect[i].mType) { + NS_WARNING(nsPrintfCString("Unexpected type for Cache schema entry %s", + name.get()).get()); + return NS_ERROR_FAILURE; + } + + if (!expect[i].mIgnoreSql && sql != expect[i].mSql) { + NS_WARNING(nsPrintfCString("Unexpected SQL for Cache schema entry %s", + name.get()).get()); + return NS_ERROR_FAILURE; + } + + foundMatch = true; + break; + } + } + + if (NS_WARN_IF(!foundMatch)) { + NS_WARNING(nsPrintfCString("Unexpected schema entry %s in Cache database", + name.get()).get()); + return NS_ERROR_FAILURE; + } + } +#endif + + return rv; +} + +} // anonymous namespace + } // namespace db } // namespace cache } // namespace dom From e8a2e90fbd50c89333eda3d32867794423ef12b1 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 43/85] Bug 1184607 P7.3 Rename Cache "max wipe version" constant to "first shipped version". r=ehsan --- dom/cache/DBAction.cpp | 2 +- dom/cache/DBSchema.cpp | 2 +- dom/cache/DBSchema.h | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dom/cache/DBAction.cpp b/dom/cache/DBAction.cpp index 709c44e0749..b35a36f2950 100644 --- a/dom/cache/DBAction.cpp +++ b/dom/cache/DBAction.cpp @@ -170,7 +170,7 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, int32_t schemaVersion = 0; rv = conn->GetSchemaVersion(&schemaVersion); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (schemaVersion > 0 && schemaVersion < db::kMaxWipeSchemaVersion) { + if (schemaVersion > 0 && schemaVersion < db::kFirstShippedSchemaVersion) { conn = nullptr; rv = WipeDatabase(dbFile, aDBDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 7e4dd5ecb3e..c72720769ba 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -31,7 +31,7 @@ namespace dom { namespace cache { namespace db { -const int32_t kMaxWipeSchemaVersion = 15; +const int32_t kFirstShippedSchemaVersion = 15; namespace { diff --git a/dom/cache/DBSchema.h b/dom/cache/DBSchema.h index 86fa097bf21..3b8a7a98410 100644 --- a/dom/cache/DBSchema.h +++ b/dom/cache/DBSchema.h @@ -116,8 +116,9 @@ StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace, nsresult IncrementalVacuum(mozIStorageConnection* aConn); -// We will wipe out databases with a schema versions less than this. -extern const int32_t kMaxWipeSchemaVersion; +// We will wipe out databases with a schema versions less than this. Newer +// versions will be migrated on open to the latest schema version. +extern const int32_t kFirstShippedSchemaVersion; } // namespace db } // namespace cache From fd7cb07044f412a1d8682a158b52c5ba574e0c04 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 44/85] Bug 1184607 P7.4 Infrastructure for running Cache schema migrations. r=ehsan --- dom/cache/DBSchema.cpp | 93 ++++++++++++++++++++++++++++++++++++++++-- dom/cache/DBSchema.h | 3 +- dom/cache/Manager.cpp | 13 ++---- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index c72720769ba..c50c670ab57 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/cache/TypeUtils.h" #include "mozIStorageConnection.h" #include "mozIStorageStatement.h" +#include "mozStorageHelper.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsCRT.h" @@ -39,8 +40,8 @@ const int32_t kLatestSchemaVersion = 15; // --------- // The following constants define the SQL schema. These are defined in the -// same order the SQL should be executed in CreateSchema(). They are broken -// out as constants for convenient use in validation and migration. +// same order the SQL should be executed in CreateOrMigrateSchema(). They are +// broken out as constants for convenient use in validation and migration. // --------- // The caches table is the single source of truth about what Cache @@ -326,10 +327,11 @@ static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn, static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut); nsresult Validate(mozIStorageConnection* aConn); +nsresult Migrate(mozIStorageConnection* aConn); } // namespace nsresult -CreateSchema(mozIStorageConnection* aConn) +CreateOrMigrateSchema(mozIStorageConnection* aConn) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aConn); @@ -347,7 +349,22 @@ CreateSchema(mozIStorageConnection* aConn) return rv; } - if (!schemaVersion) { + mozStorageTransaction trans(aConn, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + bool needVacuum = false; + + if (schemaVersion) { + // A schema exists, but its not the current version. Attempt to + // migrate it to our new schema. + rv = Migrate(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Migrations happen infrequently and reflect a chance in DB structure. + // This is a good time to rebuild the database. + needVacuum = true; + + } else { + // There is no schema installed. Create the database from scratch. rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableCaches)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -385,6 +402,15 @@ CreateSchema(mozIStorageConnection* aConn) rv = Validate(aConn); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = trans.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (needVacuum) { + // Unfortunately, this must be performed outside of the transaction. + aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM")); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + } + return rv; } @@ -2317,6 +2343,65 @@ Validate(mozIStorageConnection* aConn) return rv; } +// ----- +// Schema migration code +// ----- + +typedef nsresult (*MigrationFunc)(mozIStorageConnection*); +struct Migration +{ + Migration(int32_t aFromVersion, MigrationFunc aFunc) + : mFromVersion(aFromVersion) + , mFunc(aFunc) + { } + int32_t mFromVersion; + MigrationFunc mFunc; +}; + +// Declare migration functions here. Each function should upgrade +// the version by a single increment. Don't skip versions. + +// Configure migration functions to run for the given starting version. +Migration sMigrationList[] = { +}; + +uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration); + +nsresult +Migrate(mozIStorageConnection* aConn) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + int32_t currentVersion = 0; + nsresult rv = aConn->GetSchemaVersion(¤tVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + while (currentVersion < kLatestSchemaVersion) { + // Wiping old databases is handled in DBAction because it requires + // making a whole new mozIStorageConnection. Make sure we don't + // accidentally get here for one of those old databases. + MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion); + + for (uint32_t i = 0; i < sMigrationListLength; ++i) { + if (sMigrationList[i].mFromVersion == currentVersion) { + rv = sMigrationList[i].mFunc(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + break; + } + } + + DebugOnly lastVersion = currentVersion; + rv = aConn->GetSchemaVersion(¤tVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + MOZ_ASSERT(currentVersion > lastVersion); + } + + MOZ_ASSERT(currentVersion == kLatestSchemaVersion); + + return rv; +} + } // anonymous namespace } // namespace db diff --git a/dom/cache/DBSchema.h b/dom/cache/DBSchema.h index 3b8a7a98410..cc23f77434f 100644 --- a/dom/cache/DBSchema.h +++ b/dom/cache/DBSchema.h @@ -29,8 +29,9 @@ struct SavedResponse; namespace db { +// Note, this cannot be executed within a transaction. nsresult -CreateSchema(mozIStorageConnection* aConn); +CreateOrMigrateSchema(mozIStorageConnection* aConn); // Note, this cannot be executed within a transaction. nsresult diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 2b0cc98d2cb..8fd6d03b8d2 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -54,16 +54,9 @@ public: nsresult rv = BodyCreateDir(aDBDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - { - mozStorageTransaction trans(aConn, false, - mozIStorageConnection::TRANSACTION_IMMEDIATE); - - rv = db::CreateSchema(aConn); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - rv = trans.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - } + // executes in its own transaction + rv = db::CreateOrMigrateSchema(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // If the Context marker file exists, then the last session was // not cleanly shutdown. In these cases sqlite will ensure that From ce076455ed64cc4f7f93107a63622ec47c87c070 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 45/85] Bug 1184607 P7.5 Add RequestRedirect to Cache API schema with migration. r=ehsan --- dom/cache/CacheTypes.ipdlh | 2 + dom/cache/DBSchema.cpp | 86 +++++++++++++++++++++++++++++++++++--- dom/cache/IPCUtils.h | 5 +++ dom/cache/TypeUtils.cpp | 21 ++++++---- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/dom/cache/CacheTypes.ipdlh b/dom/cache/CacheTypes.ipdlh index bbff425d080..1f2f576f1a6 100644 --- a/dom/cache/CacheTypes.ipdlh +++ b/dom/cache/CacheTypes.ipdlh @@ -13,6 +13,7 @@ using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h"; using RequestCredentials from "mozilla/dom/cache/IPCUtils.h"; using RequestMode from "mozilla/dom/cache/IPCUtils.h"; using RequestCache from "mozilla/dom/cache/IPCUtils.h"; +using RequestRedirect from "mozilla/dom/cache/IPCUtils.h"; using ResponseType from "mozilla/dom/cache/IPCUtils.h"; using mozilla::void_t from "ipc/IPCMessageUtils.h"; using struct nsID from "nsID.h"; @@ -64,6 +65,7 @@ struct CacheRequest CacheReadStreamOrVoid body; uint32_t contentPolicyType; RequestCache requestCache; + RequestRedirect requestRedirect; }; union CacheRequestOrVoid diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index c50c670ab57..6c77a877f16 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -36,7 +36,8 @@ const int32_t kFirstShippedSchemaVersion = 15; namespace { -const int32_t kLatestSchemaVersion = 15; +// Update this whenever the DB schema is changed. +const int32_t kLatestSchemaVersion = 16; // --------- // The following constants define the SQL schema. These are defined in the @@ -105,7 +106,11 @@ const char* const kTableEntries = // Note that response_redirected_url is either going to be empty, or // it's going to be a URL different than response_url. "response_redirected_url TEXT NOT NULL, " - "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE" + "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, " + + // New columns must be added at the end of table to migrate and + // validate properly. + "request_redirect INTEGER NOT NULL" ")"; // Create an index to support the QueryCache() matching algorithm. This @@ -207,12 +212,18 @@ static_assert(int(RequestCache::Default) == 0 && int(RequestCache::Only_if_cached) == 5 && int(RequestCache::EndGuard_) == 6, "RequestCache values are as expected"); +static_assert(int(RequestRedirect::Follow) == 0 && + int(RequestRedirect::Error) == 1 && + int(RequestRedirect::Manual) == 2 && + int(RequestRedirect::EndGuard_) == 3, + "RequestRedirect values are as expected"); static_assert(int(ResponseType::Basic) == 0 && int(ResponseType::Cors) == 1 && int(ResponseType::Default) == 2 && int(ResponseType::Error) == 3 && int(ResponseType::Opaque) == 4 && - int(ResponseType::EndGuard_) == 5, + int(ResponseType::Opaqueredirect) == 5 && + int(ResponseType::EndGuard_) == 6, "ResponseType values are as expected"); // If the static_asserts below fails, it means that you have changed the @@ -360,7 +371,8 @@ CreateOrMigrateSchema(mozIStorageConnection* aConn) if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // Migrations happen infrequently and reflect a chance in DB structure. - // This is a good time to rebuild the database. + // This is a good time to rebuild the database. It also helps catch + // if a new migration is incorrect by fast failing on the corruption. needVacuum = true; } else { @@ -1580,6 +1592,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, "request_credentials, " "request_contentpolicytype, " "request_cache, " + "request_redirect, " "request_body_id, " "response_type, " "response_url, " @@ -1604,6 +1617,7 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, ":request_credentials, " ":request_contentpolicytype, " ":request_cache, " + ":request_redirect, " ":request_body_id, " ":response_type, " ":response_url, " @@ -1672,6 +1686,9 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId, static_cast(aRequest.requestCache())); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_redirect"), + static_cast(aRequest.requestRedirect())); + rv = BindId(state, NS_LITERAL_CSTRING("request_body_id"), aRequestBodyId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1951,6 +1968,7 @@ ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId, "request_credentials, " "request_contentpolicytype, " "request_cache, " + "request_redirect, " "request_body_id " "FROM entries " "WHERE id=:id;" @@ -2005,13 +2023,19 @@ ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId, aSavedRequestOut->mValue.requestCache() = static_cast(requestCache); + int32_t requestRedirect; + rv = state->GetInt32(9, &requestRedirect); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + aSavedRequestOut->mValue.requestRedirect() = + static_cast(requestRedirect); + bool nullBody = false; - rv = state->GetIsNull(9, &nullBody); + rv = state->GetIsNull(10, &nullBody); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } aSavedRequestOut->mHasBodyId = !nullBody; if (aSavedRequestOut->mHasBodyId) { - rv = ExtractId(state, 9, &aSavedRequestOut->mBodyId); + rv = ExtractId(state, 10, &aSavedRequestOut->mBodyId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } @@ -2360,9 +2384,11 @@ struct Migration // Declare migration functions here. Each function should upgrade // the version by a single increment. Don't skip versions. +nsresult MigrateFrom15To16(mozIStorageConnection* aConn); // Configure migration functions to run for the given starting version. Migration sMigrationList[] = { + Migration(15, MigrateFrom15To16), }; uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration); @@ -2402,6 +2428,54 @@ Migrate(mozIStorageConnection* aConn) return rv; } +nsresult MigrateFrom15To16(mozIStorageConnection* aConn) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + // Add the request_redirect column with a default value of "follow". Note, + // we only use a default value here because its required by ALTER TABLE and + // we need to apply the default "follow" to existing records in the table. + // We don't actually want to keep the default in the schema for future + // INSERTs. + nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE entries " + "ADD COLUMN request_redirect INTEGER NOT NULL DEFAULT 0" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Now overwrite the master SQL for the entries table to remove the column + // default value. This is also necessary for our Validate() method to + // pass on this database. + rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA writable_schema = ON" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsCOMPtr state; + rv = aConn->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE sqlite_master SET sql=:sql WHERE name='entries'" + ), getter_AddRefs(state)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("sql"), + nsDependentCString(kTableEntries)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = state->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aConn->SetSchemaVersion(16); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA writable_schema = OFF" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + return rv; +} + } // anonymous namespace } // namespace db diff --git a/dom/cache/IPCUtils.h b/dom/cache/IPCUtils.h index cb09ebe6fc6..6fe6e0b17d9 100644 --- a/dom/cache/IPCUtils.h +++ b/dom/cache/IPCUtils.h @@ -39,6 +39,11 @@ namespace IPC { mozilla::dom::RequestCache::Default, mozilla::dom::RequestCache::EndGuard_> {}; template<> + struct ParamTraits : + public ContiguousEnumSerializer {}; + template<> struct ParamTraits : public ContiguousEnumSerializerGetCredentialsMode(); aOut.contentPolicyType() = aIn->ContentPolicyType(); aOut.requestCache() = aIn->GetCacheMode(); + aOut.requestRedirect() = aIn->GetRedirectMode(); if (aBodyAction == IgnoreBody) { aOut.body() = void_t(); @@ -212,8 +213,8 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut, } } - aOut.status() = aIn.GetStatus(); - aOut.statusText() = aIn.GetStatusText(); + aOut.status() = aIn.GetUnfilteredStatus(); + aOut.statusText() = aIn.GetUnfilteredStatusText(); nsRefPtr headers = aIn.UnfilteredHeaders(); MOZ_ASSERT(headers); if (HasVaryStar(headers)) { @@ -245,7 +246,7 @@ TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv) } nsCOMPtr stream; - ir->GetInternalBody(getter_AddRefs(stream)); + ir->GetUnfilteredBody(getter_AddRefs(stream)); if (stream) { aIn.SetBodyUsed(); } @@ -304,17 +305,20 @@ TypeUtils::ToResponse(const CacheResponse& aIn) switch (aIn.type()) { - case ResponseType::Default: - break; - case ResponseType::Opaque: - ir = ir->OpaqueResponse(); - break; case ResponseType::Basic: ir = ir->BasicResponse(); break; case ResponseType::Cors: ir = ir->CORSResponse(); break; + case ResponseType::Default: + break; + case ResponseType::Opaque: + ir = ir->OpaqueResponse(); + break; + case ResponseType::Opaqueredirect: + ir = ir->OpaqueRedirectResponse(); + break; default: MOZ_CRASH("Unexpected ResponseType!"); } @@ -340,6 +344,7 @@ TypeUtils::ToInternalRequest(const CacheRequest& aIn) internalRequest->SetCredentialsMode(aIn.credentials()); internalRequest->SetContentPolicyType(aIn.contentPolicyType()); internalRequest->SetCacheMode(aIn.requestCache()); + internalRequest->SetRedirectMode(aIn.requestRedirect()); nsRefPtr internalHeaders = ToInternalHeaders(aIn.headers(), aIn.headersGuard()); From 734ffffabbbd188b13011783ea65e8b79821d7ff Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 46/85] Bug 1184607 P7.6 Expose CacheStorage .caches property on xpcshell global. r=smaug * * * 7.6 interdiff --- dom/cache/CacheStorage.cpp | 35 +++++++++++++++++++++++++++++++++++ dom/cache/CacheStorage.h | 3 +++ js/xpconnect/src/Sandbox.cpp | 6 ++++++ js/xpconnect/src/xpcprivate.h | 1 + 4 files changed, 45 insertions(+) diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp index a6fb8a51e75..ae828d7b50d 100644 --- a/dom/cache/CacheStorage.cpp +++ b/dom/cache/CacheStorage.cpp @@ -239,6 +239,41 @@ CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal, return ref.forget(); } +// static +bool +CacheStorage::DefineCaches(JSContext* aCx, JS::Handle aGlobal) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, + "Passed object is not a global object!"); + + if (NS_WARN_IF(!CacheStorageBinding::GetConstructorObject(aCx, aGlobal) || + !CacheBinding::GetConstructorObject(aCx, aGlobal))) { + return false; + } + + nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal); + MOZ_ASSERT(principal); + + ErrorResult rv; + nsRefPtr storage = + CreateOnMainThread(DEFAULT_NAMESPACE, xpc::NativeGlobal(aGlobal), principal, + false, /* private browsing */ + true, /* force trusted */ + rv); + if (NS_WARN_IF(rv.Failed())) { + return ThrowMethodFailed(aCx, rv); + } + + JS::Rooted caches(aCx); + js::AssertSameCompartment(aCx, aGlobal); + if (NS_WARN_IF(!ToJSValue(aCx, storage, &caches))) { + return false; + } + + return JS_DefineProperty(aCx, aGlobal, "caches", caches, JSPROP_ENUMERATE); +} + CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo, Feature* aFeature) : mNamespace(aNamespace) diff --git a/dom/cache/CacheStorage.h b/dom/cache/CacheStorage.h index 98a9338173f..19061ab3416 100644 --- a/dom/cache/CacheStorage.h +++ b/dom/cache/CacheStorage.h @@ -56,6 +56,9 @@ public: CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal, workers::WorkerPrivate* aWorkerPrivate, ErrorResult& aRv); + static bool + DefineCaches(JSContext* aCx, JS::Handle aGlobal); + // webidl interface methods already_AddRefed Match(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions, diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 1a08862176c..8a44da5a2af 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -29,6 +29,7 @@ #include "Crypto.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BlobBinding.h" +#include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/dom/CSSBinding.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/dom/Fetch.h" @@ -907,6 +908,8 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) #endif } else if (!strcmp(name.ptr(), "fetch")) { fetch = true; + } else if (!strcmp(name.ptr(), "caches")) { + caches = true; } else { JS_ReportError(cx, "Unknown property name: %s", name.ptr()); return false; @@ -972,6 +975,9 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) if (fetch && !SandboxCreateFetch(cx, obj)) return false; + if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj)) + return false; + return true; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 9f09a8fa449..ed61224d665 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3418,6 +3418,7 @@ struct GlobalProperties { bool crypto : 1; bool rtcIdentityProvider : 1; bool fetch : 1; + bool caches : 1; }; // Infallible. From 21e789e2d7db0b017c65e9da838fe10c96d49911 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 47/85] Bug 1184067 P7.7 Allow new Response() to be used in xpcshell tests. r=ehsan --- dom/fetch/ChannelInfo.cpp | 15 +++++++++++++++ dom/fetch/ChannelInfo.h | 2 ++ dom/fetch/Response.cpp | 13 ++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dom/fetch/ChannelInfo.cpp b/dom/fetch/ChannelInfo.cpp index 4f8d3ccf07f..69cd68eb265 100644 --- a/dom/fetch/ChannelInfo.cpp +++ b/dom/fetch/ChannelInfo.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/ChannelInfo.h" #include "nsCOMPtr.h" +#include "nsContentUtils.h" #include "nsIChannel.h" #include "nsIDocument.h" #include "nsIHttpChannel.h" @@ -68,6 +69,20 @@ ChannelInfo::InitFromChannel(nsIChannel* aChannel) mInited = true; } +void +ChannelInfo::InitFromChromeGlobal(nsIGlobalObject* aGlobal) +{ + MOZ_ASSERT(!mInited, "Cannot initialize the object twice"); + MOZ_ASSERT(aGlobal); + + MOZ_RELEASE_ASSERT( + nsContentUtils::IsSystemPrincipal(aGlobal->PrincipalOrNull())); + + mSecurityInfo.Truncate(); + mRedirected = false; + mInited = true; +} + void ChannelInfo::InitFromIPCChannelInfo(const ipc::IPCChannelInfo& aChannelInfo) { diff --git a/dom/fetch/ChannelInfo.h b/dom/fetch/ChannelInfo.h index 8bc17c6a13b..90bb924af9d 100644 --- a/dom/fetch/ChannelInfo.h +++ b/dom/fetch/ChannelInfo.h @@ -12,6 +12,7 @@ class nsIChannel; class nsIDocument; +class nsIGlobalObject; class nsIURI; namespace mozilla { @@ -69,6 +70,7 @@ public: void InitFromDocument(nsIDocument* aDoc); void InitFromChannel(nsIChannel* aChannel); + void InitFromChromeGlobal(nsIGlobalObject* aGlobal); void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo); // This restores every possible information stored from a previous channel diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index e99f8a18bc8..c0ad78108ea 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -162,12 +162,15 @@ Response::Constructor(const GlobalObject& aGlobal, // Grab a valid channel info from the global so this response is 'valid' for // interception. if (NS_IsMainThread()) { - nsCOMPtr window = do_QueryInterface(global); - MOZ_ASSERT(window); - nsIDocument* doc = window->GetExtantDoc(); - MOZ_ASSERT(doc); ChannelInfo info; - info.InitFromDocument(doc); + nsCOMPtr window = do_QueryInterface(global); + if (window) { + nsIDocument* doc = window->GetExtantDoc(); + MOZ_ASSERT(doc); + info.InitFromDocument(doc); + } else { + info.InitFromChromeGlobal(global); + } internalResponse->InitChannelInfo(info); } else { workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); From f76c498fa68d57f7ee6c9dd781de81192c4e8b16 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 48/85] Bug 1184607 P7.8 Test Cache API schema verison migrations. r=ehsan --- dom/cache/moz.build | 4 + dom/cache/test/xpcshell/head.js | 77 ++++++++++ dom/cache/test/xpcshell/make_profile.js | 142 ++++++++++++++++++ dom/cache/test/xpcshell/schema_15_profile.zip | Bin 0 -> 2577 bytes dom/cache/test/xpcshell/test_migration.js | 38 +++++ dom/cache/test/xpcshell/xpcshell.ini | 16 ++ 6 files changed, 277 insertions(+) create mode 100644 dom/cache/test/xpcshell/head.js create mode 100644 dom/cache/test/xpcshell/make_profile.js create mode 100644 dom/cache/test/xpcshell/schema_15_profile.zip create mode 100644 dom/cache/test/xpcshell/test_migration.js create mode 100644 dom/cache/test/xpcshell/xpcshell.ini diff --git a/dom/cache/moz.build b/dom/cache/moz.build index 4655b6cad90..b8c055ba6d3 100644 --- a/dom/cache/moz.build +++ b/dom/cache/moz.build @@ -100,3 +100,7 @@ MOCHITEST_CHROME_MANIFESTS += [ BROWSER_CHROME_MANIFESTS += [ 'test/mochitest/browser.ini', ] + +XPCSHELL_TESTS_MANIFESTS += [ + 'test/xpcshell/xpcshell.ini', +] diff --git a/dom/cache/test/xpcshell/head.js b/dom/cache/test/xpcshell/head.js new file mode 100644 index 00000000000..3d51929b351 --- /dev/null +++ b/dom/cache/test/xpcshell/head.js @@ -0,0 +1,77 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +// services required be initialized in order to run CacheStorage +var ss = Cc['@mozilla.org/storage/service;1'] + .createInstance(Ci.mozIStorageService); +var sts = Cc['@mozilla.org/network/stream-transport-service;1'] + .getService(Ci.nsIStreamTransportService); +var hash = Cc['@mozilla.org/security/hash;1'] + .createInstance(Ci.nsICryptoHash); + +// Expose Cache and Fetch symbols on the global +Cu.importGlobalProperties(['caches', 'fetch']); + +// Extract a zip file into the profile +function create_test_profile(zipFileName) { + do_get_profile(); + + var directoryService = Cc['@mozilla.org/file/directory_service;1'] + .getService(Ci.nsIProperties); + var profileDir = directoryService.get('ProfD', Ci.nsIFile); + var currentDir = directoryService.get('CurWorkD', Ci.nsIFile); + + var packageFile = currentDir.clone(); + packageFile.append(zipFileName); + + var zipReader = Cc['@mozilla.org/libjar/zip-reader;1'] + .createInstance(Ci.nsIZipReader); + zipReader.open(packageFile); + + var entryNames = []; + var entries = zipReader.findEntries(null); + while (entries.hasMore()) { + var entry = entries.getNext(); + entryNames.push(entry); + } + entryNames.sort(); + + for (var entryName of entryNames) { + var zipentry = zipReader.getEntry(entryName); + + var file = profileDir.clone(); + entryName.split('/').forEach(function(part) { + file.append(part); + }); + + if (zipentry.isDirectory) { + file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0755', 8)); + } else { + var istream = zipReader.getInputStream(entryName); + + var ostream = Cc['@mozilla.org/network/file-output-stream;1'] + .createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, parseInt('0644', 8), 0); + + var bostream = Cc['@mozilla.org/network/buffered-output-stream;1'] + .createInstance(Ci.nsIBufferedOutputStream); + bostream.init(ostream, 32 * 1024); + + bostream.writeFrom(istream, istream.available()); + + istream.close(); + bostream.close(); + } + } + + zipReader.close(); +} diff --git a/dom/cache/test/xpcshell/make_profile.js b/dom/cache/test/xpcshell/make_profile.js new file mode 100644 index 00000000000..b50c7aeb773 --- /dev/null +++ b/dom/cache/test/xpcshell/make_profile.js @@ -0,0 +1,142 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +// Enumerate the directory tree and store results in entryList as +// +// { path: 'a/b/c', file: } +// +// The algorithm starts with the first entry already in entryList. +function enumerate_tree(entryList) { + for (var index = 0; index < entryList.length; ++index) { + var path = entryList[index].path; + var file = entryList[index].file; + + if (file.isDirectory()) { + var dirList = file.directoryEntries; + while (dirList.hasMoreElements()) { + var dirFile = dirList.getNext().QueryInterface(Ci.nsIFile); + entryList.push({ path: path + '/' + dirFile.leafName, file: dirFile }); + } + } + } +} + +function zip_profile(zipFile, profileDir) { + var zipWriter = Cc['@mozilla.org/zipwriter;1'] + .createInstance(Ci.nsIZipWriter); + zipWriter.open(zipFile, 0x04 | 0x08 | 0x20); + + var root = profileDir.clone(); + root.append('storage'); + root.append('default'); + root.append('chrome'); + + var entryList = [{path: 'storage/default/chrome', file: root}]; + enumerate_tree(entryList); + + entryList.forEach(function(entry) { + if (entry.file.isDirectory()) { + zipWriter.addEntryDirectory(entry.path, entry.file.lastModifiedTime, + false); + } else { + var istream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + istream.init(entry.file, -1, -1, 0); + zipWriter.addEntryStream(entry.path, entry.file.lastModifiedTime, + Ci.nsIZipWriter.COMPRESSION_DEFAULT, istream, + false); + istream.close(); + } + }); + + zipWriter.close(); +} + +function exactGC() { + return new Promise(function(resolve) { + var count = 0; + function doPreciseGCandCC() { + function scheduleGCCallback() { + Cu.forceCC(); + + if (++count < 2) { + doPreciseGCandCC(); + } else { + resolve(); + } + } + Cu.schedulePreciseGC(scheduleGCCallback); + } + doPreciseGCandCC(); + }); +} + +function resetQuotaManager() { + return new Promise(function(resolve) { + var qm = Cc['@mozilla.org/dom/quota/manager;1'] + .getService(Ci.nsIQuotaManager); + + var prefService = Cc['@mozilla.org/preferences-service;1'] + .getService(Ci.nsIPrefService); + + // enable quota manager testing mode + var pref = 'dom.quotaManager.testing'; + prefService.getBranch(null).setBoolPref(pref, true); + + qm.reset(); + + // disable quota manager testing mode + //prefService.getBranch(null).setBoolPref(pref, false); + + var uri = Cc['@mozilla.org/network/io-service;1'] + .getService(Ci.nsIIOService) + .newURI('http://example.com', null, null); + var principal = Cc['@mozilla.org/scriptsecuritymanager;1'] + .getService(Ci.nsIScriptSecurityManager) + .getSystemPrincipal(); + + // use getUsageForPrincipal() to get a callback when the reset() is done + qm.getUsageForPrincipal(principal, function(principal, usage, fileUsage) { + resolve(usage); + }); + }); +} + +function run_test() { + do_test_pending(); + do_get_profile(); + + var directoryService = Cc['@mozilla.org/file/directory_service;1'] + .getService(Ci.nsIProperties); + var profileDir = directoryService.get('ProfD', Ci.nsIFile); + var currentDir = directoryService.get('CurWorkD', Ci.nsIFile); + + var zipFile = currentDir.clone(); + zipFile.append('new_profile.zip'); + if (zipFile.exists()) { + zipFile.remove(false); + } + ok(!zipFile.exists()); + + caches.open('xpcshell-test').then(function(c) { + var request = new Request('http://example.com/index.html'); + var response = new Response('hello world'); + return c.put(request, response); + }).then(exactGC).then(resetQuotaManager).then(function() { + zip_profile(zipFile, profileDir); + dump('### ### created zip at: ' + zipFile.path + '\n'); + do_test_finished(); + }).catch(function(e) { + do_test_finished(); + ok(false, e); + }); +} diff --git a/dom/cache/test/xpcshell/schema_15_profile.zip b/dom/cache/test/xpcshell/schema_15_profile.zip new file mode 100644 index 0000000000000000000000000000000000000000..6d742275b30921a721dddbcbbd12439d3081bdce GIT binary patch literal 2577 zcmbW23piA17{^a5X?4*)n-!YqiAIS@%#4UO8*Pb3jcj5x=EAr%nu|;0u8~$(w@9U2 zhB8P7wKc zA0`N(iBos>*~b1j3L1;1pg&+Rhm!3_`G&*r!9NMvL35kCS2_d#kOUqEc#4pa`18+i`)uOFpmUgu+vRcrB> z+P-&myc>VxrZWOxcW)wuMc$_x+q2BL7Z>@!2hMVI2Pz{fm26(9brf~zM5^fNNdUDi zx~O1bRD53{HIcE~^T&K~t_HZvGDFO@_TcOFNYzr-?{d|z_XtX&I<5Tv#Iak457@+I zho6iCt&?`gqsp7LM@_V>#Nc%|D;A$gt*^S#0HG3V0?G@xWE-g&knl60> zw<=ozbU6F4EwPN6no|WObqDh6Vh2OJ8YGW>7a2-I+sg*uz*GDTT?r1ni`14p%R*NF z%Rz}ht*XwbtDr3=Cug0gxQ%sqgOMOEvrw3VySlgcKB3@gd9S)<`5}RtDRix2)q^A| zv6IUmNp|*aK=E7lApEN9u*X#z-w0##p8QJ68pyt$D~^7QL@k4CA2xK0L$Sa;9Ithz z?6XkUnwTvdln)f`;GLIF_~)@8u;`gdmE_O`ZQZOoTsYu(6^r$OC~#Y6Zc| zwS!@`S z)U|4))iOSzptkSL(H>jh^zc%|8&!+UbVQ(5XK*8T^g*u6Sj_6TBc6{+ozfZ-HAQtA zVGut73y%3lbv0dl1jPw*);5KD1LsL-<$1P}pm+OPwT+_Js1Di+b8b zDtg)%&AQY(dNc1%$qS@5n-l`gAvdvos`$wC;mi7E^--%{`=U(D)9wY@?rcbHFYh*R z4|4a4*iKl|nTLOC_a|w2QX66{g%nV3r%8imt>!a4oGjS;2R)U7>aU!_EiM{@s9qaO zzHsoGWs~j^fvQO8GLVyxt$)`4vWIsZvKd6Z%?S>}k3Q4P!o`viOle=q-AA{>bpz=w z#6$_llfc6UlzOuh%^rJkR~)?ed_LRKenX>922*GY3YK=mY~oLoEOz+U4qt#aIk~+; zd0w$XGh>?;gS+f#22a}94V++S;fdcVCOMciwI!xjFg(j~zHRJreYdq3h;spjOkRFThE>>*P~*&=@qo^Jf%Vb5 z<}a8b4%DWC{a6S6_T6#B^BRfu?PRZJ{H`4Pmd2_^dhf}L=UNQOU0pZ(dq>h*ZbO3# zDFjKPWK34wU1X!85EIaSse6yZ?rh0;XQWuB5Ew8-&ydMb-HPYH(&U|4;-S(cQBxB0 z8uJ;`ULXF1!tO!(i0|NPh(A;NwvF$Xklu~R1oWwJ#I10+d|K+R8!D55r#{&{+fg$t zrg@)srmSfq7SlOvP4O|O){k1dudf;YMJ

J@R+XiGfT1FTxHas zNb;h7q9V^k8GIqi!~{N@1G)ND^{<|gE#GzISKw%yFi=l3JRWX@H^rM6nc>WEMw?+9 zVMZ9}Ml&eP69dD;p<%{&!eO-E2P+t>udYP&o?n4jR;zoz@`knBTZ?i#J0C|!j!j6o zlcVZ{fzA6yLuqz*os91?pl_SJH@DYL(?0FClQaOxjMXAM}wgM1Pd8)A-3< j{5WH$q)&vV@+zUe= literal 0 HcmV?d00001 diff --git a/dom/cache/test/xpcshell/test_migration.js b/dom/cache/test/xpcshell/test_migration.js new file mode 100644 index 00000000000..3076f3f5903 --- /dev/null +++ b/dom/cache/test/xpcshell/test_migration.js @@ -0,0 +1,38 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +function run_test() { + do_test_pending(); + create_test_profile('schema_15_profile.zip'); + + var cache; + caches.open('xpcshell-test').then(function(c) { + cache = c; + ok(cache, 'cache exists'); + return cache.keys(); + }).then(function(requestList) { + ok(requestList.length > 0, 'should have at least one request in cache'); + requestList.forEach(function(request) { + ok(request, 'each request in list should be non-null'); + ok(request.redirect === 'follow', 'request.redirect should default to "follow"'); + }); + return Promise.all(requestList.map(function(request) { + return cache.match(request); + })); + }).then(function(responseList) { + ok(responseList.length > 0, 'should have at least one response in cache'); + responseList.forEach(function(response) { + ok(response, 'each request in list should be non-null'); + }); + }).then(function() { + do_test_finished(); + }).catch(function(e) { + ok(false, 'caught exception ' + e); + do_test_finished(); + }); +} diff --git a/dom/cache/test/xpcshell/xpcshell.ini b/dom/cache/test/xpcshell/xpcshell.ini new file mode 100644 index 00000000000..2bd3f90747f --- /dev/null +++ b/dom/cache/test/xpcshell/xpcshell.ini @@ -0,0 +1,16 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +head = head.js +tail = +skip-if = toolkit == 'gonk' +support-files = + schema_15_profile.zip + +# dummy test entry to generate profile zip files +[make_profile.js] + skip-if = true + +[test_migration.js] From a5dc9c332ac71759d79de773d958334f1ea2793b Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 49/85] Bug 1184607 P8 Fix mochitests to store opaqueredirect responses in Cache for navigation URLs. r=nsm --- .../test/serviceworkers/fetch/origin/https/origin_test.js | 3 ++- .../fetch/origin/https/realindex.html^headers^ | 1 - dom/workers/test/serviceworkers/fetch/origin/origin_test.js | 6 ++++-- .../serviceworkers/fetch/origin/realindex.html^headers^ | 1 - dom/workers/test/serviceworkers/mochitest.ini | 2 -- 5 files changed, 6 insertions(+), 7 deletions(-) delete mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ delete mode 100644 dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js index ddbf02d4c6c..cf9bdae9736 100644 --- a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js +++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js @@ -4,7 +4,8 @@ self.addEventListener("install", function(event) { event.waitUntil( self.caches.open("origin-cache") .then(c => { - return c.add(prefix + 'index-https.sjs'); + return c.add(new Request(prefix + 'index-https.sjs', + { redirect: 'manual' })); }) ); }); diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ deleted file mode 100644 index 5ed82fd0658..00000000000 --- a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ +++ /dev/null @@ -1 +0,0 @@ -Access-Control-Allow-Origin: https://example.com diff --git a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js index 7b5bac22828..a68dac0eca1 100644 --- a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js +++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js @@ -6,8 +6,10 @@ self.addEventListener("install", function(event) { .then(c => { return Promise.all( [ - c.add(prefix + 'index.sjs'), - c.add(prefix + 'index-to-https.sjs'), + c.add(new Request(prefix + 'index.sjs', + { redirect: 'manual' } )), + c.add(new Request(prefix + 'index-to-https.sjs', + { redirect: 'manual' } )) ] ); }) diff --git a/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ deleted file mode 100644 index 3a6a85d8944..00000000000 --- a/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ +++ /dev/null @@ -1 +0,0 @@ -Access-Control-Allow-Origin: http://mochi.test:8888 diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index c89ca8c374f..c5110d723b3 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -53,13 +53,11 @@ support-files = fetch/origin/index.sjs fetch/origin/index-to-https.sjs fetch/origin/realindex.html - fetch/origin/realindex.html^headers^ fetch/origin/register.html fetch/origin/unregister.html fetch/origin/origin_test.js fetch/origin/https/index-https.sjs fetch/origin/https/realindex.html - fetch/origin/https/realindex.html^headers^ fetch/origin/https/register.html fetch/origin/https/unregister.html fetch/origin/https/origin_test.js From 4ddcc151429c0f8d030107f790058025e0bf0fb9 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 50/85] Bug 1184607 P9 Add wpt tests to verify service worker redirect logic. r=nsm --- .../web-platform/mozilla/meta/MANIFEST.json | 6 + .../fetch-event-redirect.https.html | 997 ++++++++++++++++++ .../fetch-event-redirect-iframe.html | 13 + .../resources/fetch-rewrite-worker.js | 32 +- .../resources/get-host-info.sub.js | 2 + .../service-worker/resources/success.py | 8 + 6 files changed, 1046 insertions(+), 12 deletions(-) create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html create mode 100644 testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py diff --git a/testing/web-platform/mozilla/meta/MANIFEST.json b/testing/web-platform/mozilla/meta/MANIFEST.json index 9f0d11e7bc3..4e676dbaa7e 100644 --- a/testing/web-platform/mozilla/meta/MANIFEST.json +++ b/testing/web-platform/mozilla/meta/MANIFEST.json @@ -142,6 +142,12 @@ "url": "/_mozilla/service-workers/service-worker/fetch-event-network-error.https.html" } ], + "service-workers/service-worker/fetch-event-redirect.https.html": [ + { + "path": "service-workers/service-worker/fetch-event-redirect.https.html", + "url": "/_mozilla/service-workers/service-worker/fetch-event-redirect.https.html" + } + ], "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html": [ { "path": "service-workers/service-worker/fetch-event-respond-with-stops-propagation.https.html", diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html new file mode 100644 index 00000000000..a6eb4bb491a --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-event-redirect.https.html @@ -0,0 +1,997 @@ + +Service Worker: Fetch Event Redirect Handling + + + + + + + + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html new file mode 100644 index 00000000000..f5a51bd591a --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html @@ -0,0 +1,13 @@ + diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js index b6fdc27705c..1b1c8192fc3 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js @@ -12,23 +12,18 @@ function get_query_params(url) { return ret; } -function get_request_init(params) { +function get_request_init(base, params) { var init = {}; - if (params['method']) { - init['method'] = params['method']; - } - if (params['mode']) { - init['mode'] = params['mode']; - } - if (params['credentials']) { - init['credentials'] = params['credentials']; - } + init['method'] = params['method'] || base['method']; + init['mode'] = params['mode'] || base['mode']; + init['credentials'] = params['credentials'] || base['credentials']; + init['redirect'] = params['redirect'] || base['redirect']; return init; } self.addEventListener('fetch', function(event) { var params = get_query_params(event.request.url); - var init = get_request_init(params); + var init = get_request_init(event.request, params); var url = params['url']; if (params['ignore']) { return; @@ -62,6 +57,19 @@ self.addEventListener('fetch', function(event) { if (url) { request = new Request(url, init); } - fetch(request).then(resolve, reject); + fetch(request).then(function(response) { + var expectedType = params['expected_type']; + if (expectedType && response.type !== expectedType) { + // Resolve a JSON object with a failure instead of rejecting + // in order to distinguish this from a NetworkError, which + // may be expected even if the type is correct. + resolve(new Response(JSON.stringify({ + result: 'failure', + detail: 'got ' + response.type + ' Response.type instead of ' + + expectedType + }))); + } + resolve(response); + }, reject) })); }); diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js index 2bc2af2272b..b64334df667 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/get-host-info.sub.js @@ -17,8 +17,10 @@ function get_host_info() { return { HTTP_ORIGIN: 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT, HTTPS_ORIGIN: 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT, + HTTPS_ORIGIN_WITH_CREDS: 'https://foo:bar@' + ORIGINAL_HOST + ':' + HTTPS_PORT, HTTP_REMOTE_ORIGIN: 'http://' + REMOTE_HOST + ':' + HTTP_PORT, HTTPS_REMOTE_ORIGIN: 'https://' + REMOTE_HOST + ':' + HTTPS_PORT, + HTTPS_REMOTE_ORIGIN_WITH_CREDS: 'https://foo:bar@' + REMOTE_HOST + ':' + HTTPS_PORT, UNAUTHENTICATED_ORIGIN: 'http://' + UNAUTHENTICATED_HOST + ':' + HTTP_PORT }; } diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py new file mode 100644 index 00000000000..bcbb487d2b2 --- /dev/null +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/success.py @@ -0,0 +1,8 @@ +def main(request, response): + headers = [] + + if "ACAOrigin" in request.GET: + for item in request.GET["ACAOrigin"].split(","): + headers.append(("Access-Control-Allow-Origin", item)) + + return headers, "{ \"result\": \"success\" }" From 18f1602420f5eb0fb15c6efc773052ea45c44d97 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 51/85] Bug 1184607 P10 Expose channel security info during e10s redirect. Support security info in redirecting interceptions. r=jduell --- netwerk/protocol/http/HttpChannelChild.cpp | 27 ++++++++++++++----- netwerk/protocol/http/HttpChannelChild.h | 6 +++-- netwerk/protocol/http/HttpChannelParent.cpp | 30 ++++++++++++++------- netwerk/protocol/http/HttpChannelParent.h | 2 ++ netwerk/protocol/http/PHttpChannel.ipdl | 3 ++- netwerk/protocol/http/nsHttpChannel.cpp | 8 +++++- netwerk/protocol/http/nsHttpChannel.h | 1 + 7 files changed, 57 insertions(+), 20 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 4cbd8c760dc..6599d7de128 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -1032,17 +1032,19 @@ class Redirect1Event : public ChannelEvent const uint32_t& newChannelId, const URIParams& newURI, const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead) + const nsHttpResponseHead& responseHead, + const nsACString& securityInfoSerialization) : mChild(child) , mNewChannelId(newChannelId) , mNewURI(newURI) , mRedirectFlags(redirectFlags) - , mResponseHead(responseHead) {} + , mResponseHead(responseHead) + , mSecurityInfoSerialization(securityInfoSerialization) {} void Run() { mChild->Redirect1Begin(mNewChannelId, mNewURI, mRedirectFlags, - mResponseHead); + mResponseHead, mSecurityInfoSerialization); } private: HttpChannelChild* mChild; @@ -1050,20 +1052,25 @@ class Redirect1Event : public ChannelEvent URIParams mNewURI; uint32_t mRedirectFlags; nsHttpResponseHead mResponseHead; + nsCString mSecurityInfoSerialization; }; bool HttpChannelChild::RecvRedirect1Begin(const uint32_t& newChannelId, const URIParams& newUri, const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead) + const nsHttpResponseHead& responseHead, + const nsCString& securityInfoSerialization) { + // TODO: handle security info LOG(("HttpChannelChild::RecvRedirect1Begin [this=%p]\n", this)); if (mEventQ->ShouldEnqueue()) { mEventQ->Enqueue(new Redirect1Event(this, newChannelId, newUri, - redirectFlags, responseHead)); + redirectFlags, responseHead, + securityInfoSerialization)); } else { - Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead); + Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead, + securityInfoSerialization); } return true; } @@ -1072,7 +1079,8 @@ void HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, const URIParams& newUri, const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead) + const nsHttpResponseHead& responseHead, + const nsACString& securityInfoSerialization) { LOG(("HttpChannelChild::Redirect1Begin [this=%p]\n", this)); @@ -1105,6 +1113,11 @@ HttpChannelChild::Redirect1Begin(const uint32_t& newChannelId, // We won't get OnStartRequest, set cookies here. mResponseHead = new nsHttpResponseHead(responseHead); + if (!securityInfoSerialization.IsEmpty()) { + NS_DeserializeObject(securityInfoSerialization, + getter_AddRefs(mSecurityInfo)); + } + bool rewriteToGET = HttpBaseChannel::ShouldRewriteRedirectToGET(mResponseHead->Status(), mRequestHead.ParsedMethod()); diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 84b62f44b94..fdabaf345bb 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -135,7 +135,8 @@ protected: bool RecvRedirect1Begin(const uint32_t& newChannel, const URIParams& newURI, const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead) override; + const nsHttpResponseHead& responseHead, + const nsCString& securityInfoSerialization) override; bool RecvRedirect3Complete() override; bool RecvAssociateApplicationCache(const nsCString& groupID, const nsCString& clientID) override; @@ -239,7 +240,8 @@ private: void Redirect1Begin(const uint32_t& newChannelId, const URIParams& newUri, const uint32_t& redirectFlags, - const nsHttpResponseHead& responseHead); + const nsHttpResponseHead& responseHead, + const nsACString& securityInfoSerialization); void Redirect3Complete(); void DeleteSelf(); diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 6febd5687f4..cb581501723 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -394,6 +394,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI, if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) { mSynthesizedResponseHead = new nsHttpResponseHead(aSynthesizedResponseHead.get_nsHttpResponseHead()); mShouldIntercept = true; + mChannel->SetCouldBeSynthesized(); if (!aSecurityInfoSerialization.IsEmpty()) { nsCOMPtr secInfo; @@ -836,14 +837,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) chan->GetStatus(&channelStatus); nsCString secInfoSerialization; - nsCOMPtr secInfoSupp; - chan->GetSecurityInfo(getter_AddRefs(secInfoSupp)); - if (secInfoSupp) { - mAssociatedContentSecurity = do_QueryInterface(secInfoSupp); - nsCOMPtr secInfoSer = do_QueryInterface(secInfoSupp); - if (secInfoSer) - NS_SerializeToString(secInfoSer, secInfoSerialization); - } + UpdateAndSerializeSecurityInfo(secInfoSerialization); uint16_t redirectCount = 0; mChannel->GetRedirectCount(&redirectCount); @@ -1048,10 +1042,14 @@ HttpChannelParent::StartRedirect(uint32_t newChannelId, URIParams uriParams; SerializeURI(newURI, uriParams); + nsCString secInfoSerialization; + UpdateAndSerializeSecurityInfo(secInfoSerialization); + nsHttpResponseHead *responseHead = mChannel->GetResponseHead(); bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags, responseHead ? *responseHead - : nsHttpResponseHead()); + : nsHttpResponseHead(), + secInfoSerialization); if (!result) { // Bug 621446 investigation mSentRedirect1BeginFailed = true; @@ -1326,6 +1324,20 @@ HttpChannelParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid, return NS_OK; } +void +HttpChannelParent::UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut) +{ + nsCOMPtr secInfoSupp; + mChannel->GetSecurityInfo(getter_AddRefs(secInfoSupp)); + if (secInfoSupp) { + mAssociatedContentSecurity = do_QueryInterface(secInfoSupp); + nsCOMPtr secInfoSer = do_QueryInterface(secInfoSupp); + if (secInfoSer) { + NS_SerializeToString(secInfoSer, aSerializedSecurityInfoOut); + } + } +} + //----------------------------------------------------------------------------- // HttpChannelSecurityWarningReporter //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index 3c28b906c76..ec48b72abcb 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -158,6 +158,8 @@ protected: const nsAString& aMessageCategory) override; private: + void UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut); + nsRefPtr mChannel; nsCOMPtr mCacheEntry; nsCOMPtr mAssociatedContentSecurity; diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index b7718aa12b7..4ead186b286 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -121,7 +121,8 @@ child: Redirect1Begin(uint32_t newChannelId, URIParams newUri, uint32_t redirectFlags, - nsHttpResponseHead responseHead); + nsHttpResponseHead responseHead, + nsCString securityInfoSerialization); // Called if redirect successful so that child can complete setup. Redirect3Complete(); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 36090fc4ef2..86c0105012a 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -4986,7 +4986,7 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) if (ShouldIntercept()) { mInterceptCache = MAYBE_INTERCEPT; - mResponseCouldBeSynthesized = true; + SetCouldBeSynthesized(); } // Remember the cookie header that was set, if any @@ -6966,5 +6966,11 @@ nsHttpChannel::OnPush(const nsACString &url, Http2PushedStream *pushedStream) return rv; } +void +nsHttpChannel::SetCouldBeSynthesized() +{ + mResponseCouldBeSynthesized = true; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 325fac114e9..311829a4749 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -235,6 +235,7 @@ public: /* internal necko use only */ void MarkIntercepted(); NS_IMETHOD GetResponseSynthesized(bool* aSynthesized) override; bool AwaitingCacheCallbacks(); + void SetCouldBeSynthesized(); protected: virtual ~nsHttpChannel(); From e5309ad38146380246d3f26a7a886d6eaf56a919 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 52/85] Bug 1184607 P11 Fix fetch CORS tests to not expect same-origin requests that redirect to preflight. r=nsm --- dom/tests/mochitest/fetch/test_fetch_cors.js | 32 +++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dom/tests/mochitest/fetch/test_fetch_cors.js b/dom/tests/mochitest/fetch/test_fetch_cors.js index 641eba501ff..9d31d60ba75 100644 --- a/dom/tests/mochitest/fetch/test_fetch_cors.js +++ b/dom/tests/mochitest/fetch/test_fetch_cors.js @@ -1125,7 +1125,7 @@ function testRedirects() { }, ], }, - { pass: 0, + { pass: 1, method: "POST", body: "hi there", headers: { "Content-Type": "text/plain", @@ -1140,6 +1140,24 @@ function testRedirects() { ], }, { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "my-header": "myValue", + }, + hops: [{ server: "http://mochi.test:8888", + }, + { server: "http://test1.example.com", + allowOrigin: origin, + allowHeaders: "my-header", + }, + { server: "http://test2.example.com", + allowOrigin: origin, + allowHeaders: "my-header", + } + ], + }, + { pass: 1, method: "DELETE", hops: [{ server: "http://mochi.test:8888", }, @@ -1148,6 +1166,18 @@ function testRedirects() { }, ], }, + { pass: 0, + method: "DELETE", + hops: [{ server: "http://mochi.test:8888", + }, + { server: "http://test1.example.com", + allowOrigin: origin, + }, + { server: "http://test2.example.com", + allowOrigin: origin, + }, + ], + }, { pass: 0, method: "POST", body: "hi there", From b08c6bd7ba440882259ca2c9e2fa5f78e4d08309 Mon Sep 17 00:00:00 2001 From: Chris Manchester Date: Mon, 31 Aug 2015 14:28:47 -0700 Subject: [PATCH 53/85] Bug 1000922 - Use nsMainThreadPtrHandle instead of already_AddRefed and forget for callbacks in NativeOSFileInternals.cpp r=jdm --- .../osfile/NativeOSFileInternals.cpp | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/toolkit/components/osfile/NativeOSFileInternals.cpp b/toolkit/components/osfile/NativeOSFileInternals.cpp index 5f9282a1bd0..0b912a0b756 100644 --- a/toolkit/components/osfile/NativeOSFileInternals.cpp +++ b/toolkit/components/osfile/NativeOSFileInternals.cpp @@ -407,8 +407,8 @@ public: * alread_AddRefed to ensure that we do not manipulate main-thread * only refcounters off the main thread. */ - ErrorEvent(already_AddRefed&& aOnSuccess, - already_AddRefed&& aOnError, + ErrorEvent(nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError, already_AddRefed& aDiscardedResult, const nsACString& aOperation, int32_t aOSError) @@ -433,13 +433,13 @@ public: return NS_OK; } private: - // The callbacks. Maintained as nsRefPtr as they are generally + // The callbacks. Maintained as nsMainThreadPtrHandle as they are generally // xpconnect values, which cannot be manipulated with nsCOMPtr off // the main thread. We store both the success callback and the // error callback to ensure that they are safely released on the // main thread. - nsRefPtr mOnSuccess; - nsRefPtr mOnError; + nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnError; nsRefPtr mDiscardedResult; int32_t mOSError; nsCString mOperation; @@ -461,8 +461,8 @@ public: * we do not manipulate xpconnect refcounters off the main thread * (which is illegal). */ - SuccessEvent(already_AddRefed&& aOnSuccess, - already_AddRefed&& aOnError, + SuccessEvent(nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError, already_AddRefed& aResult) : mOnSuccess(aOnSuccess) , mOnError(aOnError) @@ -483,13 +483,13 @@ public: return NS_OK; } private: - // The callbacks. Maintained as nsRefPtr as they are generally + // The callbacks. Maintained as nsMainThreadPtrHandle as they are generally // xpconnect values, which cannot be manipulated with nsCOMPtr off // the main thread. We store both the success callback and the // error callback to ensure that they are safely released on the // main thread. - nsRefPtr mOnSuccess; - nsRefPtr mOnError; + nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnError; nsRefPtr mResult; }; @@ -501,8 +501,8 @@ public: */ class AbstractDoEvent: public nsRunnable { public: - AbstractDoEvent(already_AddRefed& aOnSuccess, - already_AddRefed& aOnError) + AbstractDoEvent(nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError) : mOnSuccess(aOnSuccess) , mOnError(aOnError) #if defined(DEBUG) @@ -519,8 +519,8 @@ public: already_AddRefed&& aDiscardedResult, int32_t aOSError = 0) { Resolve(); - nsRefPtr event = new ErrorEvent(mOnSuccess.forget(), - mOnError.forget(), + nsRefPtr event = new ErrorEvent(mOnSuccess, + mOnError, aDiscardedResult, aOperation, aOSError); @@ -539,8 +539,8 @@ public: */ void Succeed(already_AddRefed&& aResult) { Resolve(); - nsRefPtr event = new SuccessEvent(mOnSuccess.forget(), - mOnError.forget(), + nsRefPtr event = new SuccessEvent(mOnSuccess, + mOnError, aResult); nsresult rv = NS_DispatchToMainThread(event); if (NS_FAILED(rv)) { @@ -566,8 +566,8 @@ private: } private: - nsRefPtr mOnSuccess; - nsRefPtr mOnError; + nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnError; #if defined(DEBUG) // |true| once the action is complete bool mResolved; @@ -587,8 +587,8 @@ public: */ AbstractReadEvent(const nsAString& aPath, const uint64_t aBytes, - already_AddRefed& aOnSuccess, - already_AddRefed& aOnError) + nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError) : AbstractDoEvent(aOnSuccess, aOnError) , mPath(aPath) , mBytes(aBytes) @@ -736,8 +736,8 @@ class DoReadToTypedArrayEvent final : public AbstractReadEvent { public: DoReadToTypedArrayEvent(const nsAString& aPath, const uint32_t aBytes, - already_AddRefed&& aOnSuccess, - already_AddRefed&& aOnError) + nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError) : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError) , mResult(new TypedArrayResult(TimeStamp::Now())) @@ -774,8 +774,8 @@ public: DoReadToStringEvent(const nsAString& aPath, const nsACString& aEncoding, const uint32_t aBytes, - already_AddRefed&& aOnSuccess, - already_AddRefed&& aOnError) + nsMainThreadPtrHandle& aOnSuccess, + nsMainThreadPtrHandle& aOnError) : AbstractReadEvent(aPath, aBytes, aOnSuccess, aOnError) , mEncoding(aEncoding) , mResult(new StringResult(TimeStamp::Now())) @@ -890,17 +890,21 @@ NativeOSFileInternalsService::Read(const nsAString& aPath, // Prepare the off main thread event and dispatch it nsCOMPtr onSuccess(aOnSuccess); + nsMainThreadPtrHandle onSuccessHandle( + new nsMainThreadPtrHolder(onSuccess)); nsCOMPtr onError(aOnError); + nsMainThreadPtrHandle onErrorHandle( + new nsMainThreadPtrHolder(onError)); nsRefPtr event; if (encoding.IsEmpty()) { event = new DoReadToTypedArrayEvent(aPath, bytes, - onSuccess.forget(), - onError.forget()); + onSuccessHandle, + onErrorHandle); } else { event = new DoReadToStringEvent(aPath, encoding, bytes, - onSuccess.forget(), - onError.forget()); + onSuccessHandle, + onErrorHandle); } nsresult rv; From a5a9d60f68c5e1f4e9c26ecb7bffb0cb46a3ed3f Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Mon, 31 Aug 2015 23:34:59 +0200 Subject: [PATCH 54/85] Bug 1182515 - WebSocketChannel leaked when WebSocketChannel::AsyncOpen fails, r=mcmanus --- .../protocol/websocket/WebSocketChannel.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 07737043023..66877bb698f 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -3265,6 +3265,26 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI, // nsIChannelEventSink in this object in order to deal with redirects localChannel->SetNotificationCallbacks(this); + class MOZ_STACK_CLASS CleanUpOnFailure + { + public: + explicit CleanUpOnFailure(WebSocketChannel* aWebSocketChannel) + : mWebSocketChannel(aWebSocketChannel) + {} + + ~CleanUpOnFailure() + { + if (!mWebSocketChannel->mWasOpened) { + mWebSocketChannel->mChannel = nullptr; + mWebSocketChannel->mHttpChannel = nullptr; + } + } + + WebSocketChannel *mWebSocketChannel; + }; + + CleanUpOnFailure cuof(this); + mChannel = do_QueryInterface(localChannel, &rv); NS_ENSURE_SUCCESS(rv, rv); From ba103561d2ad4ab03341f1c33b9ec1d48b3bf94a Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 31 Aug 2015 18:36:48 -0400 Subject: [PATCH 55/85] Backed out changeset 066e84750afd for landing with the wrong bug number. --- dom/fetch/ChannelInfo.cpp | 15 --------------- dom/fetch/ChannelInfo.h | 2 -- dom/fetch/Response.cpp | 13 +++++-------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/dom/fetch/ChannelInfo.cpp b/dom/fetch/ChannelInfo.cpp index 69cd68eb265..4f8d3ccf07f 100644 --- a/dom/fetch/ChannelInfo.cpp +++ b/dom/fetch/ChannelInfo.cpp @@ -6,7 +6,6 @@ #include "mozilla/dom/ChannelInfo.h" #include "nsCOMPtr.h" -#include "nsContentUtils.h" #include "nsIChannel.h" #include "nsIDocument.h" #include "nsIHttpChannel.h" @@ -69,20 +68,6 @@ ChannelInfo::InitFromChannel(nsIChannel* aChannel) mInited = true; } -void -ChannelInfo::InitFromChromeGlobal(nsIGlobalObject* aGlobal) -{ - MOZ_ASSERT(!mInited, "Cannot initialize the object twice"); - MOZ_ASSERT(aGlobal); - - MOZ_RELEASE_ASSERT( - nsContentUtils::IsSystemPrincipal(aGlobal->PrincipalOrNull())); - - mSecurityInfo.Truncate(); - mRedirected = false; - mInited = true; -} - void ChannelInfo::InitFromIPCChannelInfo(const ipc::IPCChannelInfo& aChannelInfo) { diff --git a/dom/fetch/ChannelInfo.h b/dom/fetch/ChannelInfo.h index 90bb924af9d..8bc17c6a13b 100644 --- a/dom/fetch/ChannelInfo.h +++ b/dom/fetch/ChannelInfo.h @@ -12,7 +12,6 @@ class nsIChannel; class nsIDocument; -class nsIGlobalObject; class nsIURI; namespace mozilla { @@ -70,7 +69,6 @@ public: void InitFromDocument(nsIDocument* aDoc); void InitFromChannel(nsIChannel* aChannel); - void InitFromChromeGlobal(nsIGlobalObject* aGlobal); void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo); // This restores every possible information stored from a previous channel diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index c0ad78108ea..e99f8a18bc8 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -162,15 +162,12 @@ Response::Constructor(const GlobalObject& aGlobal, // Grab a valid channel info from the global so this response is 'valid' for // interception. if (NS_IsMainThread()) { - ChannelInfo info; nsCOMPtr window = do_QueryInterface(global); - if (window) { - nsIDocument* doc = window->GetExtantDoc(); - MOZ_ASSERT(doc); - info.InitFromDocument(doc); - } else { - info.InitFromChromeGlobal(global); - } + MOZ_ASSERT(window); + nsIDocument* doc = window->GetExtantDoc(); + MOZ_ASSERT(doc); + ChannelInfo info; + info.InitFromDocument(doc); internalResponse->InitChannelInfo(info); } else { workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); From 3e155da8861f08cf3ba4248a311439ff6504ecde Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 31 Aug 2015 14:26:30 -0700 Subject: [PATCH 56/85] Bug 1184607 P7.7 Allow new Response() to be used in xpcshell tests. r=ehsan --- dom/fetch/ChannelInfo.cpp | 15 +++++++++++++++ dom/fetch/ChannelInfo.h | 2 ++ dom/fetch/Response.cpp | 13 ++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dom/fetch/ChannelInfo.cpp b/dom/fetch/ChannelInfo.cpp index 4f8d3ccf07f..69cd68eb265 100644 --- a/dom/fetch/ChannelInfo.cpp +++ b/dom/fetch/ChannelInfo.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/ChannelInfo.h" #include "nsCOMPtr.h" +#include "nsContentUtils.h" #include "nsIChannel.h" #include "nsIDocument.h" #include "nsIHttpChannel.h" @@ -68,6 +69,20 @@ ChannelInfo::InitFromChannel(nsIChannel* aChannel) mInited = true; } +void +ChannelInfo::InitFromChromeGlobal(nsIGlobalObject* aGlobal) +{ + MOZ_ASSERT(!mInited, "Cannot initialize the object twice"); + MOZ_ASSERT(aGlobal); + + MOZ_RELEASE_ASSERT( + nsContentUtils::IsSystemPrincipal(aGlobal->PrincipalOrNull())); + + mSecurityInfo.Truncate(); + mRedirected = false; + mInited = true; +} + void ChannelInfo::InitFromIPCChannelInfo(const ipc::IPCChannelInfo& aChannelInfo) { diff --git a/dom/fetch/ChannelInfo.h b/dom/fetch/ChannelInfo.h index 8bc17c6a13b..90bb924af9d 100644 --- a/dom/fetch/ChannelInfo.h +++ b/dom/fetch/ChannelInfo.h @@ -12,6 +12,7 @@ class nsIChannel; class nsIDocument; +class nsIGlobalObject; class nsIURI; namespace mozilla { @@ -69,6 +70,7 @@ public: void InitFromDocument(nsIDocument* aDoc); void InitFromChannel(nsIChannel* aChannel); + void InitFromChromeGlobal(nsIGlobalObject* aGlobal); void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo); // This restores every possible information stored from a previous channel diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index e99f8a18bc8..c0ad78108ea 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -162,12 +162,15 @@ Response::Constructor(const GlobalObject& aGlobal, // Grab a valid channel info from the global so this response is 'valid' for // interception. if (NS_IsMainThread()) { - nsCOMPtr window = do_QueryInterface(global); - MOZ_ASSERT(window); - nsIDocument* doc = window->GetExtantDoc(); - MOZ_ASSERT(doc); ChannelInfo info; - info.InitFromDocument(doc); + nsCOMPtr window = do_QueryInterface(global); + if (window) { + nsIDocument* doc = window->GetExtantDoc(); + MOZ_ASSERT(doc); + info.InitFromDocument(doc); + } else { + info.InitFromChromeGlobal(global); + } internalResponse->InitChannelInfo(info); } else { workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); From 8c990f5a2c76eba1d6dae2605ef612c86851b393 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Mon, 31 Aug 2015 18:33:47 -0400 Subject: [PATCH 57/85] Backed out changeset af935eae9bed (bug 1194818) for Windows build bustage. CLOSED TREE --- netwerk/protocol/http/Http2Session.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index b14ebaa5857..9094a27c8d2 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -1152,12 +1152,9 @@ Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength if (mInputFrameFlags & kFlag_PADDED) { paddingLength = *reinterpret_cast(mInputFrameBuffer + kFrameHeaderBytes); paddingControlBytes = 1; - } else { - paddingLength = 0; - paddingControlBytes = 0; } - if ((paddingLength + paddingControlBytes) > mInputFrameDataSize) { + if (paddingLength > mInputFrameDataSize) { // This is fatal to the session LOG3(("Http2Session::ParsePadding %p stream 0x%x PROTOCOL_ERROR " "paddingLength %d > frame size %d\n", @@ -1215,11 +1212,6 @@ Http2Session::RecvHeaders(Http2Session *self) paddingLength, self->mInputFrameFlags & kFlag_PADDED)); - if ((paddingControlBytes + priorityLen + paddingLength) > self->mInputFrameDataSize) { - // This is fatal to the session - RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); - } - if (!self->mInputFrameDataStream) { // Cannot find stream. We can continue the session, but we need to // uncompress the header block to maintain the correct compression context From 661217ec10aafd927fe629998f5066e56ec99362 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 1 Sep 2015 10:47:07 +1200 Subject: [PATCH 58/85] Bug 1199878: [MSE/webm] Properly calculate media segment duration. r=kinetik We can know with certainty the duration of a block if we have a following one. We do not have to always rely on having a previous segment to estimate the duration. --- dom/media/mediasource/ContainerParser.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dom/media/mediasource/ContainerParser.cpp b/dom/media/mediasource/ContainerParser.cpp index 52250f92619..c3716cfb222 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -271,7 +271,6 @@ public: mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset, mapping[0].mEndOffset); } - mLastMapping = Some(mapping[completeIdx]); if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) { // We now have all information required to delimit a complete cluster. @@ -288,12 +287,24 @@ public: mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset)); } - if (!completeIdx) { + Maybe previousMapping; + if (completeIdx) { + previousMapping = Some(mapping[completeIdx - 1]); + } else { + previousMapping = mLastMapping; + } + + mLastMapping = Some(mapping[completeIdx]); + + if (!previousMapping && completeIdx + 1u >= mapping.Length()) { + // We have no previous nor next block available, + // so we can't estimate this block's duration. return false; } - uint64_t frameDuration = - mapping[completeIdx].mTimecode - mapping[completeIdx - 1].mTimecode; + uint64_t frameDuration = (completeIdx + 1u < mapping.Length()) + ? mapping[completeIdx + 1].mTimecode - mapping[completeIdx].mTimecode + : mapping[completeIdx].mTimecode - previousMapping.ref().mTimecode; aStart = mapping[0].mTimecode / NS_PER_USEC; aEnd = (mapping[completeIdx].mTimecode + frameDuration) / NS_PER_USEC; From 99fb2040fe059ab73068e5685518b15f294860e7 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 1 Sep 2015 10:47:07 +1200 Subject: [PATCH 59/85] Bug 1199878: [webm] P2. Hold one frames for which the duration can't be known or estimated. r=kinetik --- dom/media/webm/WebMDemuxer.cpp | 32 ++++++++++++++++++++------------ dom/media/webm/WebMDemuxer.h | 5 ++--- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index f8c387cdd37..504bfa4c396 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -131,8 +131,6 @@ WebMDemuxer::WebMDemuxer(MediaResource* aResource, bool aIsMediaSource) , mVideoTrack(0) , mAudioTrack(0) , mSeekPreroll(0) - , mLastAudioFrameTime(0) - , mLastVideoFrameTime(0) , mAudioCodec(-1) , mVideoCodec(-1) , mHasVideo(false) @@ -507,27 +505,37 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl // the timestamp of the next packet for this track. If we've reached the // end of the resource, use the file's duration as the end time of this // video frame. - int64_t next_tstamp = 0; + int64_t next_tstamp = INT64_MIN; if (aType == TrackInfo::kAudioTrack) { nsRefPtr next_holder(NextPacket(aType)); if (next_holder) { next_tstamp = next_holder->Timestamp(); PushAudioPacket(next_holder); - } else { + } else if (!mIsMediaSource || + (mIsMediaSource && mLastAudioFrameTime.isSome())) { next_tstamp = tstamp; - next_tstamp += tstamp - mLastAudioFrameTime; + next_tstamp += tstamp - mLastAudioFrameTime.refOr(0); + } else { + PushAudioPacket(holder); } - mLastAudioFrameTime = tstamp; + mLastAudioFrameTime = Some(tstamp); } else if (aType == TrackInfo::kVideoTrack) { nsRefPtr next_holder(NextPacket(aType)); if (next_holder) { next_tstamp = next_holder->Timestamp(); PushVideoPacket(next_holder); - } else { + } else if (!mIsMediaSource || + (mIsMediaSource && mLastVideoFrameTime.isSome())) { next_tstamp = tstamp; - next_tstamp += tstamp - mLastVideoFrameTime; + next_tstamp += tstamp - mLastVideoFrameTime.refOr(0); + } else { + PushVideoPacket(holder); } - mLastVideoFrameTime = tstamp; + mLastVideoFrameTime = Some(tstamp); + } + + if (mIsMediaSource && next_tstamp == INT64_MIN) { + return false; } int64_t discardPadding = 0; @@ -657,7 +665,7 @@ WebMDemuxer::GetNextKeyframeTime() EnsureUpToDateIndex(); uint64_t keyframeTime; uint64_t lastFrame = - media::TimeUnit::FromMicroseconds(mLastVideoFrameTime).ToNanoseconds(); + media::TimeUnit::FromMicroseconds(mLastVideoFrameTime.refOr(0)).ToNanoseconds(); if (!mBufferedState->GetNextKeyframeTime(lastFrame, &keyframeTime) || keyframeTime <= lastFrame) { return -1; @@ -725,8 +733,8 @@ WebMDemuxer::SeekInternal(const media::TimeUnit& aTarget) WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset); } - mLastAudioFrameTime = 0; - mLastVideoFrameTime = 0; + mLastAudioFrameTime.reset(); + mLastVideoFrameTime.reset(); return NS_OK; } diff --git a/dom/media/webm/WebMDemuxer.h b/dom/media/webm/WebMDemuxer.h index 8c9d2e1a565..3d28568b8c7 100644 --- a/dom/media/webm/WebMDemuxer.h +++ b/dom/media/webm/WebMDemuxer.h @@ -156,11 +156,10 @@ private: // Nanoseconds to discard after seeking. uint64_t mSeekPreroll; - int64_t mLastAudioFrameTime; - // Calculate the frame duration from the last decodeable frame using the // previous frame's timestamp. In NS. - int64_t mLastVideoFrameTime; + Maybe mLastAudioFrameTime; + Maybe mLastVideoFrameTime; // Codec ID of audio track int mAudioCodec; From aba7c66dff86ee6f359d3b90f5e7e8aab7058e57 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 1 Sep 2015 09:24:37 +1000 Subject: [PATCH 60/85] Bug 1197765 - Compare text content inside frame instead of the content node for ruby autohiding. r=dbaron --- dom/base/nsContentUtils.h | 2 ++ layout/base/nsLayoutUtils.cpp | 23 +++++++++++++++++++ layout/base/nsLayoutUtils.h | 20 ++++++++++++++++ layout/generic/nsRubyBaseContainerFrame.cpp | 8 +++---- .../w3c-css/submitted/ruby/reftest.list | 1 + .../submitted/ruby/ruby-autohide-004.html | 15 ++++++++++++ 6 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 5b25ca05868..b08b586693b 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1312,6 +1312,8 @@ public: * are not converted into newlines. Only textnodes and cdata nodes are * added to the result. * + * @see nsLayoutUtils::GetFrameTextContent + * * @param aNode Node to get textual contents of. * @param aDeep If true child elements of aNode are recursivly descended * into to find text children. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index bd577cf1a62..cec4d749275 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8577,3 +8577,26 @@ nsLayoutUtils::ShouldUseNoFramesSheet(nsIDocument* aDocument) } return !allowSubframes; } + +/* static */ void +nsLayoutUtils::GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult) +{ + aResult.Truncate(); + AppendFrameTextContent(aFrame, aResult); +} + +/* static */ void +nsLayoutUtils::AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult) +{ + if (aFrame->GetType() == nsGkAtoms::textFrame) { + auto textFrame = static_cast(aFrame); + auto offset = textFrame->GetContentOffset(); + auto length = textFrame->GetContentLength(); + textFrame->GetContent()-> + GetText()->AppendTo(aResult, offset, length); + } else { + for (nsIFrame* child : aFrame->PrincipalChildList()) { + AppendFrameTextContent(child, aResult); + } + } +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 7c1840c6f60..d7737cccb2d 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2719,6 +2719,26 @@ public: static bool ShouldUseNoScriptSheet(nsIDocument* aDocument); static bool ShouldUseNoFramesSheet(nsIDocument* aDocument); + /** + * Get the text content inside the frame. This methods traverse the + * frame tree and collect the content from text frames. Note that this + * method is similiar to nsContentUtils::GetNodeTextContent, but it at + * least differs from that method in the following things: + * 1. it skips text content inside nodes like style, script, textarea + * which don't generate an in-tree text frame for the text; + * 2. it skips elements with display property set to none; + * 3. it skips out-of-flow elements; + * 4. it includes content inside pseudo elements; + * 5. it may include part of text content of a node if a text frame + * inside is split to different continuations. + */ + static void GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult); + + /** + * Same as GetFrameTextContent but appends the result rather than sets it. + */ + static void AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult); + private: static uint32_t sFontSizeInflationEmPerLine; static uint32_t sFontSizeInflationMinTwips; diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 5933eccf6a5..be33805ab77 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -13,7 +13,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/WritingModes.h" -#include "nsContentUtils.h" +#include "nsLayoutUtils.h" #include "nsLineLayout.h" #include "nsPresContext.h" #include "nsStyleContext.h" @@ -609,8 +609,7 @@ nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, nsAutoString baseText; if (aColumn.mBaseFrame) { - nsContentUtils::GetNodeTextContent(aColumn.mBaseFrame->GetContent(), - true, baseText); + nsLayoutUtils::GetFrameTextContent(aColumn.mBaseFrame, baseText); } // Reflow text frames @@ -618,8 +617,7 @@ nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, nsRubyTextFrame* textFrame = aColumn.mTextFrames[i]; if (textFrame) { nsAutoString annotationText; - nsContentUtils::GetNodeTextContent(textFrame->GetContent(), - true, annotationText); + nsLayoutUtils::GetFrameTextContent(textFrame, annotationText); // Per CSS Ruby spec, the content comparison for auto-hiding // takes place prior to white spaces collapsing (white-space) diff --git a/layout/reftests/w3c-css/submitted/ruby/reftest.list b/layout/reftests/w3c-css/submitted/ruby/reftest.list index c12e992e360..1883fb98869 100644 --- a/layout/reftests/w3c-css/submitted/ruby/reftest.list +++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list @@ -9,4 +9,5 @@ == ruby-autohide-001.html ruby-autohide-001-ref.html == ruby-autohide-002.html ruby-autohide-002-ref.html == ruby-autohide-003.html ruby-autohide-003-ref.html +== ruby-autohide-004.html ruby-autohide-001-ref.html diff --git a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html new file mode 100644 index 00000000000..4499cf3e16f --- /dev/null +++ b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html @@ -0,0 +1,15 @@ + + + + + CSS Test: Autohide ruby annotations which are identical to their bases + + + + + + + 振 + + + From a00d5ba2f996eebc87cb2836bf9487ce2eba5c80 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 1 Sep 2015 10:22:45 +1000 Subject: [PATCH 61/85] Bug 1200458 - Skip permission check for .hgrc on Windows in hgsetup wizard. r=gps DONTBUILD --- tools/mercurial/hgsetup/wizard.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/mercurial/hgsetup/wizard.py b/tools/mercurial/hgsetup/wizard.py index f7a1bdfe9dd..8a339e92be7 100644 --- a/tools/mercurial/hgsetup/wizard.py +++ b/tools/mercurial/hgsetup/wizard.py @@ -444,18 +444,19 @@ class MercurialSetupWizard(object): c.write(sys.stdout) return 1 - # Config file may contain sensitive content, such as passwords. - # Prompt to remove global permissions. - mode = os.stat(config_path).st_mode - if mode & (stat.S_IRWXG | stat.S_IRWXO): - print(FILE_PERMISSIONS_WARNING) - if self._prompt_yn('Remove permissions for others to read ' - 'your hgrc file'): - # We don't care about sticky and set UID bits because this is - # a regular file. - mode = mode & stat.S_IRWXU - print('Changing permissions of %s' % config_path) - os.chmod(config_path, mode) + if sys.platform != 'win32': + # Config file may contain sensitive content, such as passwords. + # Prompt to remove global permissions. + mode = os.stat(config_path).st_mode + if mode & (stat.S_IRWXG | stat.S_IRWXO): + print(FILE_PERMISSIONS_WARNING) + if self._prompt_yn('Remove permissions for others to ' + 'read your hgrc file'): + # We don't care about sticky and set UID bits because + # this is a regular file. + mode = mode & stat.S_IRWXU + print('Changing permissions of %s' % config_path) + os.chmod(config_path, mode) print(FINISHED) return 0 From bdacacf087d4c00270c0f8957687bc4ce8ca7a25 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 12:50:20 -0400 Subject: [PATCH 62/85] Bug 1200208 - Send the audio-playback notification when the page calls HTMLMediaElement::Play() before the metadata has been fully loaded; r=baku --- dom/base/test/mochitest.ini | 2 + .../test_audioNotificationWithEarlyPlay.html | 73 +++++++++++++++++++ dom/html/HTMLMediaElement.cpp | 69 +++++++++++++++--- dom/html/HTMLMediaElement.h | 16 ++-- 4 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 dom/base/test/test_audioNotificationWithEarlyPlay.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 9a102c6b6f9..e8023134bc8 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -267,6 +267,8 @@ support-files = skip-if = buildapp == 'mulet' [test_audioNotificationStopOnNavigation.html] skip-if = buildapp == 'mulet' +[test_audioNotificationWithEarlyPlay.html] +skip-if = buildapp == 'mulet' [test_bug1091883.html] [test_bug116083.html] [test_bug793311.html] diff --git a/dom/base/test/test_audioNotificationWithEarlyPlay.html b/dom/base/test/test_audioNotificationWithEarlyPlay.html new file mode 100644 index 00000000000..66184ae03ab --- /dev/null +++ b/dom/base/test/test_audioNotificationWithEarlyPlay.html @@ -0,0 +1,73 @@ + + + + Test for audio controller in windows + + + + +
+
+ + + + + diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 2e47c094648..540feb3e434 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -116,6 +116,34 @@ static PRLogModuleInfo* gMediaElementEventsLog; using namespace mozilla::layers; using mozilla::net::nsMediaFragmentURIParser; +class MOZ_STACK_CLASS AutoNotifyAudioChannelAgent +{ + nsRefPtr mElement; + bool mShouldNotify; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; +public: + AutoNotifyAudioChannelAgent(mozilla::dom::HTMLMediaElement* aElement, + bool aNotify + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mElement(aElement) + , mShouldNotify(aNotify) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (mShouldNotify) { + mElement->NotifyAudioChannelAgent(false); + } + } + ~AutoNotifyAudioChannelAgent() + { + if (mShouldNotify) { + // The audio channel agent is destroyed at this point. + if (mElement->MaybeCreateAudioChannelAgent()) { + mElement->NotifyAudioChannelAgent(true); + } + } + } +}; + namespace mozilla { namespace dom { @@ -3189,6 +3217,14 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo, { MOZ_ASSERT(NS_IsMainThread()); + // If the element is gaining or losing an audio track, we need to notify + // the audio channel agent so that the correct audio-playback events will + // get dispatched. + bool audioTrackChanging = mMediaInfo.HasAudio() != aInfo->HasAudio(); + AutoNotifyAudioChannelAgent autoNotify(this, + audioTrackChanging && + mPlayingThroughTheAudioChannel); + mMediaInfo = *aInfo; mIsEncrypted = aInfo->IsEncrypted() #ifdef MOZ_EME @@ -4488,7 +4524,25 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted) return NS_OK; } -void HTMLMediaElement::UpdateAudioChannelPlayingState() +bool +HTMLMediaElement::MaybeCreateAudioChannelAgent() +{ + if (!mAudioChannelAgent) { + nsresult rv; + mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + MOZ_ASSERT(mAudioChannelAgent); + mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), + static_cast(mAudioChannel), + this); + } + return true; +} + +void +HTMLMediaElement::UpdateAudioChannelPlayingState() { bool playingThroughTheAudioChannel = (!mPaused && @@ -4506,18 +4560,9 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState() return; } - if (!mAudioChannelAgent) { - nsresult rv; - mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv); - if (!mAudioChannelAgent) { - return; - } - mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(), - static_cast(mAudioChannel), - this); + if (MaybeCreateAudioChannelAgent()) { + NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } - - NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel); } } diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index 664b508dc4b..f277b41eb57 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -35,10 +35,6 @@ // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ -class nsIChannel; -class nsIHttpChannel; -class nsILoadGroup; - typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; @@ -56,9 +52,13 @@ class MediaTrack; } // namespace dom } // namespace mozilla +class AutoNotifyAudioChannelAgent; +class nsIChannel; +class nsIHttpChannel; +class nsILoadGroup; +class nsIRunnable; class nsITimer; class nsRange; -class nsIRunnable; namespace mozilla { namespace dom { @@ -78,6 +78,8 @@ class HTMLMediaElement : public nsGenericHTMLElement, public MediaDecoderOwner, public nsIAudioChannelAgentCallback { + friend AutoNotifyAudioChannelAgent; + public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::layers::ImageContainer ImageContainer; @@ -1050,6 +1052,10 @@ protected: // Notifies the audio channel agent when the element starts or stops playing. void NotifyAudioChannelAgent(bool aPlaying); + // Creates the audio channel agent if needed. Returns true if the audio + // channel agent is ready to be used. + bool MaybeCreateAudioChannelAgent(); + class nsAsyncEventRunner; using nsGenericHTMLElement::DispatchEvent; // For nsAsyncEventRunner. From 3b51ca2fa8c8171732702bf499fab30155f6d727 Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Tue, 1 Sep 2015 02:47:23 +0200 Subject: [PATCH 63/85] Bug 1186160 - WebSocketChannel accesses nsDocShell and nsDocument off the main thread, r=mcmanus --- netwerk/protocol/websocket/WebSocketChannel.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 66877bb698f..1aa7e81d30d 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -2360,8 +2360,8 @@ WebSocketChannel::AbortSession(nsresult reason) return; mStopped = 1; - if (mTransport && reason != NS_BASE_STREAM_CLOSED && - !mRequestedClose && !mClientClosed && !mServerClosed) { + if (mTransport && reason != NS_BASE_STREAM_CLOSED && !mRequestedClose && + !mClientClosed && !mServerClosed && mConnecting == NOT_CONNECTING) { mRequestedClose = 1; mStopOnClose = reason; mSocketThread->Dispatch( @@ -3121,7 +3121,11 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI, { LOG(("WebSocketChannel::AsyncOpen() %p\n", this)); - MOZ_ASSERT(NS_IsMainThread(), "not main thread"); + if (!NS_IsMainThread()) { + MOZ_ASSERT(false, "not main thread"); + LOG(("WebSocketChannel::AsyncOpen() called off the main thread")); + return NS_ERROR_UNEXPECTED; + } if (!aURI || !aListener) { LOG(("WebSocketChannel::AsyncOpen() Uri or Listener null")); @@ -3349,7 +3353,7 @@ WebSocketChannel::Close(uint16_t code, const nsACString & reason) mScriptCloseReason = reason; mScriptCloseCode = code; - if (!mTransport) { + if (!mTransport || mConnecting != NOT_CONNECTING) { nsresult rv; if (code == CLOSE_GOING_AWAY) { // Not an error: for example, tab has closed or navigated away @@ -3397,6 +3401,11 @@ WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary, { MOZ_ASSERT(IsOnTargetThread(), "not target thread"); + if (!mDataStarted) { + LOG(("WebSocketChannel:: Error: data not started yet\n")); + return NS_ERROR_UNEXPECTED; + } + if (mRequestedClose) { LOG(("WebSocketChannel:: Error: send when closed\n")); return NS_ERROR_UNEXPECTED; From 4380c8a5ed0d024716e7c755521a53786518351e Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 31 Aug 2015 11:34:57 -0700 Subject: [PATCH 64/85] Bug 1200482: Add comment to js/public/UbiNode.h warning about operating on graphs constructed by hostile code. DONTBUILD r=fitzgen --- js/public/UbiNode.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 6fb69d99aee..75514267cf3 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -97,7 +97,6 @@ // represented by a "rope", a structure that points to the two original // strings. // -// // We intend to use ubi::Node to write tools that report memory usage, so it's // important that ubi::Node accurately portray how much memory nodes consume. // Thus, for example, when data that apparently belongs to multiple nodes is @@ -142,6 +141,25 @@ // If this restriction prevents us from implementing interesting tools, we may // teach the GC how to root ubi::Nodes, fix up hash tables that use them as // keys, etc. +// +// +// Hostile Graph Structure +// +// Analyses consuming ubi::Node graphs must be robust when presented with graphs +// that are deliberately constructed to exploit their weaknesses. When operating +// on live graphs, web content has control over the object graph, and less +// direct control over shape and string structure, and analyses should be +// prepared to handle extreme cases gracefully. For example, if an analysis were +// to use the C++ stack in a depth-first traversal, carefully constructed +// content could cause the analysis to overflow the stack. +// +// When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses +// must be even more careful: since snapshots often come from potentially +// compromised e10s content processes, even properties normally guaranteed by +// the platform (the proper linking of DOM nodes, for example) might be +// corrupted. While it is the deserializer's responsibility to check the basic +// structure of the snapshot file, the analyses should be prepared for ubi::Node +// graphs constructed from snapshots to be even more bizarre. class JSAtom; From 911fc14a24b5e031c5483de34b3327c5e3b36bb9 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 31 Aug 2015 20:17:59 -0700 Subject: [PATCH 65/85] Backed out changeset 297155cba060 for landing with the wrong bug number. --- js/public/UbiNode.h | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 75514267cf3..6fb69d99aee 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -97,6 +97,7 @@ // represented by a "rope", a structure that points to the two original // strings. // +// // We intend to use ubi::Node to write tools that report memory usage, so it's // important that ubi::Node accurately portray how much memory nodes consume. // Thus, for example, when data that apparently belongs to multiple nodes is @@ -141,25 +142,6 @@ // If this restriction prevents us from implementing interesting tools, we may // teach the GC how to root ubi::Nodes, fix up hash tables that use them as // keys, etc. -// -// -// Hostile Graph Structure -// -// Analyses consuming ubi::Node graphs must be robust when presented with graphs -// that are deliberately constructed to exploit their weaknesses. When operating -// on live graphs, web content has control over the object graph, and less -// direct control over shape and string structure, and analyses should be -// prepared to handle extreme cases gracefully. For example, if an analysis were -// to use the C++ stack in a depth-first traversal, carefully constructed -// content could cause the analysis to overflow the stack. -// -// When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses -// must be even more careful: since snapshots often come from potentially -// compromised e10s content processes, even properties normally guaranteed by -// the platform (the proper linking of DOM nodes, for example) might be -// corrupted. While it is the deserializer's responsibility to check the basic -// structure of the snapshot file, the analyses should be prepared for ubi::Node -// graphs constructed from snapshots to be even more bizarre. class JSAtom; From 1fd3d53abeab73546b8607296c76290eead566a9 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 31 Aug 2015 11:34:57 -0700 Subject: [PATCH 66/85] Bug 1200345: Add comment to js/public/UbiNode.h warning about operating on graphs constructed by hostile code. DONTBUILD r=fitzgen --- js/public/UbiNode.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 6fb69d99aee..75514267cf3 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -97,7 +97,6 @@ // represented by a "rope", a structure that points to the two original // strings. // -// // We intend to use ubi::Node to write tools that report memory usage, so it's // important that ubi::Node accurately portray how much memory nodes consume. // Thus, for example, when data that apparently belongs to multiple nodes is @@ -142,6 +141,25 @@ // If this restriction prevents us from implementing interesting tools, we may // teach the GC how to root ubi::Nodes, fix up hash tables that use them as // keys, etc. +// +// +// Hostile Graph Structure +// +// Analyses consuming ubi::Node graphs must be robust when presented with graphs +// that are deliberately constructed to exploit their weaknesses. When operating +// on live graphs, web content has control over the object graph, and less +// direct control over shape and string structure, and analyses should be +// prepared to handle extreme cases gracefully. For example, if an analysis were +// to use the C++ stack in a depth-first traversal, carefully constructed +// content could cause the analysis to overflow the stack. +// +// When ubi::Nodes refer to nodes deserialized from a heap snapshot, analyses +// must be even more careful: since snapshots often come from potentially +// compromised e10s content processes, even properties normally guaranteed by +// the platform (the proper linking of DOM nodes, for example) might be +// corrupted. While it is the deserializer's responsibility to check the basic +// structure of the snapshot file, the analyses should be prepared for ubi::Node +// graphs constructed from snapshots to be even more bizarre. class JSAtom; From fbee31325f9f6c50d90b7e9f3bb09bed6a5b7684 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 20:19:24 -0400 Subject: [PATCH 67/85] Bug 1199929 - Don't show the keyboard shortcut in the tooltip text for the tab audio indicator on background tabs; r=jaws --- browser/base/content/tabbrowser.xml | 16 ++++++++++---- .../test/general/browser_audioTabIcon.js | 21 +++++++++++++------ .../chrome/browser/tabbrowser.properties | 2 ++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index adebf416809..a30718eabe1 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3958,10 +3958,18 @@ stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") : this.mStringBundle.getString("tabs.closeTab.tooltip"); } else if (tab._overPlayingIcon) { - let stringID = tab.linkedBrowser.audioMuted ? - "tabs.unmuteAudio.tooltip" : - "tabs.muteAudio.tooltip"; - label = stringWithShortcut(stringID, "key_toggleMute"); + let stringID; + if (tab.selected) { + stringID = tab.linkedBrowser.audioMuted ? + "tabs.unmuteAudio.tooltip" : + "tabs.muteAudio.tooltip"; + label = stringWithShortcut(stringID, "key_toggleMute"); + } else { + stringID = tab.linkedBrowser.audioMuted ? + "tabs.unmuteAudio.background.tooltip" : + "tabs.muteAudio.background.tooltip"; + label = this.mStringBundle.getString(stringID); + } } else { label = tab.getAttribute("label") + (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : ""); diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index 9cafc0cb8c9..59a0f2304b1 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -36,11 +36,19 @@ function leave_icon(icon) { disable_non_test_mouse(false); } -function* test_tooltip(icon, expectedTooltip) { +function* test_tooltip(icon, expectedTooltip, isActiveTab) { let tooltip = document.getElementById("tabbrowser-tab-tooltip"); yield hover_icon(icon, tooltip); - is(tooltip.getAttribute("label").indexOf(expectedTooltip), 0, "Correct tooltip expected"); + if (isActiveTab) { + // The active tab should have the keybinding shortcut in the tooltip. + // We check this by ensuring that the strings are not equal but the expected + // message appears in the beginning. + isnot(tooltip.getAttribute("label"), expectedTooltip, "Tooltips should not be equal"); + is(tooltip.getAttribute("label").indexOf(expectedTooltip), 0, "Correct tooltip expected"); + } else { + is(tooltip.getAttribute("label"), expectedTooltip, "Tooltips should not be equal"); + } leave_icon(icon); } @@ -100,6 +108,7 @@ function* test_muting_using_menu(tab, expectMuted) { function* test_playing_icon_on_tab(tab, browser, isPinned) { let icon = document.getAnonymousElementByAttribute(tab, "anonid", isPinned ? "overlay-icon" : "soundplaying-icon"); + let isActiveTab = tab === gBrowser.selectedTab; yield ContentTask.spawn(browser, {}, function* () { let audio = content.document.querySelector("audio"); @@ -108,7 +117,7 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { yield wait_for_tab_playing_event(tab, true); - yield test_tooltip(icon, "Mute tab"); + yield test_tooltip(icon, "Mute tab", isActiveTab); ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted"); @@ -116,13 +125,13 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { ok("muted" in get_tab_attributes(tab), "Muted attribute should be persisted"); - yield test_tooltip(icon, "Unmute tab"); + yield test_tooltip(icon, "Unmute tab", isActiveTab); yield test_mute_tab(tab, icon, false); ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted"); - yield test_tooltip(icon, "Mute tab"); + yield test_tooltip(icon, "Mute tab", isActiveTab); yield test_mute_tab(tab, icon, true); @@ -135,7 +144,7 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { ok(tab.hasAttribute("muted") && !tab.hasAttribute("soundplaying"), "Tab should still be muted but not playing"); - yield test_tooltip(icon, "Unmute tab"); + yield test_tooltip(icon, "Unmute tab", isActiveTab); yield test_mute_tab(tab, icon, false); diff --git a/browser/locales/en-US/chrome/browser/tabbrowser.properties b/browser/locales/en-US/chrome/browser/tabbrowser.properties index a48951a51bd..71ee61d4771 100644 --- a/browser/locales/en-US/chrome/browser/tabbrowser.properties +++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties @@ -40,3 +40,5 @@ tabs.muteAudio.tooltip=Mute tab (%S) # LOCALIZATION NOTE (tabs.unmuteAudio.tooltip): # %S is the keyboard shortcut for "Unmute tab" tabs.unmuteAudio.tooltip=Unmute tab (%S) +tabs.muteAudio.background.tooltip=Mute tab +tabs.unmuteAudio.background.tooltip=Unmute tab From 4c6a1380a259091b603be057b824c5b3dc0c6b15 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 21:51:16 -0400 Subject: [PATCH 68/85] Bug 1200381 - Enable themes to customize the appearance of the toggle mute tab context menu; r=jaws This is done by setting the muted and soundplaying attributes on the context menu item, similar to what has been done for the tab. --- browser/base/content/browser.js | 30 +++++++++++++++++++ .../test/general/browser_audioTabIcon.js | 24 +++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index ff35ed857d6..b392303083f 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7577,6 +7577,17 @@ function restoreLastSession() { var TabContextMenu = { contextTab: null, + _updateToggleMuteMenuItem(aTab, aConditionFn) { + ["muted", "soundplaying"].forEach(attr => { + if (!aConditionFn || aConditionFn(attr)) { + if (aTab.hasAttribute(attr)) { + aTab.toggleMuteMenuItem.setAttribute(attr, "true"); + } else { + aTab.toggleMuteMenuItem.removeAttribute(attr); + } + } + }); + }, updateContextMenu: function updateContextMenu(aPopupMenu) { this.contextTab = aPopupMenu.triggerNode.localName == "tab" ? aPopupMenu.triggerNode : gBrowser.selectedTab; @@ -7637,6 +7648,25 @@ var TabContextMenu = { toggleMute.label = gNavigatorBundle.getString("muteTab.label"); toggleMute.accessKey = gNavigatorBundle.getString("muteTab.accesskey"); } + + this.contextTab.toggleMuteMenuItem = toggleMute; + this._updateToggleMuteMenuItem(this.contextTab); + + this.contextTab.addEventListener("TabAttrModified", this, false); + aPopupMenu.addEventListener("popuphiding", this, false); + }, + handleEvent(aEvent) { + switch (aEvent.type) { + case "popuphiding": + gBrowser.removeEventListener("TabAttrModified", this); + aEvent.target.removeEventListener("popuphiding", this); + break; + case "TabAttrModified": + let tab = aEvent.target; + this._updateToggleMuteMenuItem(tab, + attr => aEvent.detail.changed.indexOf(attr) >= 0); + break; + } } }; diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index 59a0f2304b1..fec775627f0 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -97,6 +97,30 @@ function* test_muting_using_menu(tab, expectMuted) { is(toggleMute.label, expectedLabel, "Correct label expected"); is(toggleMute.accessKey, "M", "Correct accessKey expected"); + is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); + ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); + + let browser = tab.linkedBrowser; + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.play(); + }); + + yield wait_for_tab_playing_event(tab, true); + + is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); + ok(toggleMute.hasAttribute("soundplaying"), "Should have the soundplaying attribute"); + + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.pause(); + }); + + yield wait_for_tab_playing_event(tab, false); + + is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); + ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); + // Click on the menu and wait for the tab to be muted. let mutedPromise = get_wait_for_mute_promise(tab, !expectMuted); let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden"); From 0bb9e87851d54adfe3fc5582b3562f9029d709e0 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 22:05:03 -0400 Subject: [PATCH 69/85] Bug 1200487 - Refactor playing and pausing a tab in browser_audioTabIcon.js; r=jaws --- .../test/general/browser_audioTabIcon.js | 113 ++++++------------ 1 file changed, 38 insertions(+), 75 deletions(-) diff --git a/browser/base/content/test/general/browser_audioTabIcon.js b/browser/base/content/test/general/browser_audioTabIcon.js index fec775627f0..1363f6fa462 100644 --- a/browser/base/content/test/general/browser_audioTabIcon.js +++ b/browser/base/content/test/general/browser_audioTabIcon.js @@ -10,6 +10,26 @@ function* wait_for_tab_playing_event(tab, expectPlaying) { }); } +function* play(tab) { + let browser = tab.linkedBrowser; + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.play(); + }); + + yield wait_for_tab_playing_event(tab, true); +} + +function* pause(tab) { + let browser = tab.linkedBrowser; + yield ContentTask.spawn(browser, {}, function* () { + let audio = content.document.querySelector("audio"); + audio.pause(); + }); + + yield wait_for_tab_playing_event(tab, false); +} + function disable_non_test_mouse(disable) { let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); @@ -100,23 +120,12 @@ function* test_muting_using_menu(tab, expectMuted) { is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); - let browser = tab.linkedBrowser; - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - yield wait_for_tab_playing_event(tab, true); + yield play(tab); is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); ok(toggleMute.hasAttribute("soundplaying"), "Should have the soundplaying attribute"); - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); - - yield wait_for_tab_playing_event(tab, false); + yield pause(tab); is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute"); ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute"); @@ -134,12 +143,7 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { isPinned ? "overlay-icon" : "soundplaying-icon"); let isActiveTab = tab === gBrowser.selectedTab; - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - yield wait_for_tab_playing_event(tab, true); + yield play(tab); yield test_tooltip(icon, "Mute tab", isActiveTab); @@ -159,11 +163,7 @@ function* test_playing_icon_on_tab(tab, browser, isPinned) { yield test_mute_tab(tab, icon, true); - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); - yield wait_for_tab_playing_event(tab, false); + yield pause(tab); ok(tab.hasAttribute("muted") && !tab.hasAttribute("soundplaying"), "Tab should still be muted but not playing"); @@ -217,11 +217,7 @@ function* test_swapped_browser(oldTab, newBrowser, isPlaying) { function* test_browser_swapping(tab, browser) { // First, test swapping with a playing but muted tab. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - yield wait_for_tab_playing_event(tab, true); + yield play(tab); let icon = document.getAnonymousElementByAttribute(tab, "anonid", "soundplaying-icon"); @@ -236,10 +232,7 @@ function* test_browser_swapping(tab, browser) { // Now, test swapping with a muted but not playing tab. // Note that the tab remains muted, so we only need to pause playback. tab = gBrowser.getTabForBrowser(newBrowser); - yield ContentTask.spawn(newBrowser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); + yield pause(tab); yield BrowserTestUtils.withNewTab({ gBrowser, @@ -259,24 +252,15 @@ function* test_click_on_pinned_tab_after_mute() { // Pin the tab. gBrowser.pinTab(tab); - // Start playbak. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - // Wait for playback to start. - yield wait_for_tab_playing_event(tab, true); + // Start playback and wait for it to finish. + yield play(tab); // Mute the tab. let icon = document.getAnonymousElementByAttribute(tab, "anonid", "overlay-icon"); yield test_mute_tab(tab, icon, true); - // Stop playback - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); + // Pause playback and wait for it to finish. + yield pause(tab); // Unmute tab. yield test_mute_tab(tab, icon, false); @@ -305,14 +289,8 @@ function* test_cross_process_load() { function* test_on_browser(browser) { let tab = gBrowser.getTabForBrowser(browser); - // Start playback. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - // Wait for playback to start. - yield wait_for_tab_playing_event(tab, true); + // Start playback and wait for it to finish. + yield play(tab); let soundPlayingStoppedPromise = BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, event => event.detail.changed.indexOf("soundplaying") >= 0 @@ -348,23 +326,14 @@ function* test_mute_keybinding() { // Make sure it's possible to mute before the tab is playing. yield test_muting_using_keyboard(tab); - // Start playback. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - // Wait for playback to start. - yield wait_for_tab_playing_event(tab, true); + // Start playback and wait for it to finish. + yield play(tab); // Make sure it's possible to mute after the tab is playing. yield test_muting_using_keyboard(tab); - // Start playback. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.pause(); - }); + // Pause playback and wait for it to finish. + yield pause(tab); // Make sure things work if the tab is pinned. gBrowser.pinTab(tab); @@ -372,14 +341,8 @@ function* test_mute_keybinding() { // Make sure it's possible to mute before the tab is playing. yield test_muting_using_keyboard(tab); - // Start playback. - yield ContentTask.spawn(browser, {}, function* () { - let audio = content.document.querySelector("audio"); - audio.play(); - }); - - // Wait for playback to start. - yield wait_for_tab_playing_event(tab, true); + // Start playback and wait for it to finish. + yield play(tab); // Make sure it's possible to mute after the tab is playing. yield test_muting_using_keyboard(tab); From a6c017958ac630ad1369df48151560a0c0fedbc9 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 1 Sep 2015 12:02:55 +0800 Subject: [PATCH 70/85] Bug 1179547 - release decoders when tests finish as well as cubeb streams to reduce the number of concurrent cubeb streams and the chance of OpenCubeb() errors. r=kinetik. --- dom/media/test/test_fragment_play.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dom/media/test/test_fragment_play.html b/dom/media/test/test_fragment_play.html index 3726e87a6fe..13dfef6313b 100644 --- a/dom/media/test/test_fragment_play.html +++ b/dom/media/test/test_fragment_play.html @@ -11,7 +11,7 @@
 
+  
+
+
+
+
+
+
diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js
index 4856bcfd338..ee573e4b57d 100644
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -77,6 +77,8 @@ function BrowserElementParent() {
   Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true);
+  Services.obs.addObserver(this, 'frameloader-message-manager-will-change', /* ownsWeak = */ true);
+  Services.obs.addObserver(this, 'frameloader-message-manager-changed', /* ownsWeak = */ true);
 }
 
 BrowserElementParent.prototype = {
@@ -161,10 +163,17 @@ BrowserElementParent.prototype = {
 
   _setupMessageListener: function() {
     this._mm = this._frameLoader.messageManager;
-    let self = this;
-    let isWidget = this._frameLoader
-                       .QueryInterface(Ci.nsIFrameLoader)
-                       .ownerIsWidget;
+    this._isWidget = this._frameLoader
+                         .QueryInterface(Ci.nsIFrameLoader)
+                         .ownerIsWidget;
+    this._mm.addMessageListener('browser-element-api:call', this);
+    this._mm.loadFrameScript("chrome://global/content/extensions.js", true);
+  },
+
+  receiveMessage: function(aMsg) {
+    if (!this._isAlive()) {
+      return;
+    }
 
     // Messages we receive are handed to functions which take a (data) argument,
     // where |data| is the message manager's data object.
@@ -222,20 +231,15 @@ BrowserElementParent.prototype = {
       "opentab": this._fireEventFromMsg
     };
 
-    this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
-      if (!self._isAlive()) {
-        return;
-      }
+    if (aMsg.data.msg_name in mmCalls) {
+      mmCalls[aMsg.data.msg_name].apply(this, arguments);
+    } else if (!this._isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
+      mmSecuritySensitiveCalls[aMsg.data.msg_name].apply(this, arguments);
+    }
+  },
 
-      if (aMsg.data.msg_name in mmCalls) {
-        return mmCalls[aMsg.data.msg_name].apply(self, arguments);
-      } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
-        return mmSecuritySensitiveCalls[aMsg.data.msg_name]
-                 .apply(self, arguments);
-      }
-    });
-
-    this._mm.loadFrameScript("chrome://global/content/extensions.js", true);
+  _removeMessageListener: function() {
+    this._mm.removeMessageListener('browser-element-api:call', this);
   },
 
   /**
@@ -1105,6 +1109,16 @@ BrowserElementParent.prototype = {
         this._sendAsyncMsg('copypaste-do-command', { command: data });
       }
       break;
+    case 'frameloader-message-manager-will-change':
+      if (this._isAlive() && subject == this._frameLoader) {
+        this._removeMessageListener();
+      }
+      break;
+    case 'frameloader-message-manager-changed':
+      if (this._isAlive() && subject == this._frameLoader) {
+        this._setupMessageListener();
+      }
+      break;
     default:
       debug('Unknown topic: ' + topic);
       break;
diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp
index 0bf2a46e72b..9920825d513 100644
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -54,6 +54,10 @@ nsBrowserElement::IsNotWidgetOrThrow(ErrorResult& aRv)
 void
 nsBrowserElement::InitBrowserElementAPI()
 {
+  if (mBrowserElementAPI) {
+    return;
+  }
+
   bool isBrowserOrApp;
   nsCOMPtr frameLoader = GetFrameLoader();
   NS_ENSURE_TRUE_VOID(frameLoader);
diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp
index c4c82df953d..03e2acad953 100644
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -278,6 +278,7 @@ TabParent::TabParent(nsIContentParent* aManager,
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
+  , mIsDetached(true)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
@@ -472,6 +473,35 @@ TabParent::Destroy()
   mMarkedDestroying = true;
 }
 
+void
+TabParent::Detach()
+{
+  if (mIsDetached) {
+    return;
+  }
+  RemoveWindowListeners();
+  if (RenderFrameParent* frame = GetRenderFrame()) {
+    RemoveTabParentFromTable(frame->GetLayersId());
+  }
+  mIsDetached = true;
+}
+
+void
+TabParent::Attach(nsFrameLoader* aFrameLoader)
+{
+  MOZ_ASSERT(mIsDetached);
+  if (!mIsDetached) {
+    return;
+  }
+  Element* ownerElement = aFrameLoader->GetOwnerContent();
+  SetOwnerElement(ownerElement);
+  if (RenderFrameParent* frame = GetRenderFrame()) {
+    AddTabParentToTable(frame->GetLayersId(), this);
+    frame->OwnerContentChanged(ownerElement);
+  }
+  mIsDetached = false;
+}
+
 bool
 TabParent::Recv__delete__()
 {
diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h
index c8b61d1cbef..b20784924bd 100644
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -122,6 +122,8 @@ public:
     nsIXULBrowserWindow* GetXULBrowserWindow();
 
     void Destroy();
+    void Detach();
+    void Attach(nsFrameLoader* aFrameLoader);
 
     void RemoveWindowListeners();
     void AddWindowListeners();
@@ -515,6 +517,8 @@ private:
     bool mMarkedDestroying;
     // When true, the TabParent is invalid and we should not send IPC messages anymore.
     bool mIsDestroyed;
+    // When true, the TabParent is detached from the frame loader.
+    bool mIsDetached;
     // Whether we have already sent a FileDescriptor for the app package.
     bool mAppPackageFileDescriptorSent;
 

From 834a8ed2358ffaa69fdc67c4ebbce2feb3328f9d Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Mon, 31 Aug 2015 16:53:13 -0700
Subject: [PATCH 72/85] Bug 1200097 - Fix warnings in logalloc. r=glandium.

---
 memory/replace/logalloc/FdPrintf.cpp     | 3 ++-
 memory/replace/logalloc/moz.build        | 3 ---
 memory/replace/logalloc/replay/moz.build | 3 ---
 3 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/memory/replace/logalloc/FdPrintf.cpp b/memory/replace/logalloc/FdPrintf.cpp
index 50d98dc40fd..4e4cde4a0de 100644
--- a/memory/replace/logalloc/FdPrintf.cpp
+++ b/memory/replace/logalloc/FdPrintf.cpp
@@ -13,6 +13,7 @@
 #endif
 #include 
 #include "mozilla/Assertions.h"
+#include "mozilla/unused.h"
 
 /* Template class allowing a limited number of increments on a value */
 template 
@@ -125,7 +126,7 @@ out:
   DWORD written;
   WriteFile(reinterpret_cast(aFd), buf, b - buf, &written, nullptr);
 #else
-  write(aFd, buf, b - buf);
+  mozilla::unused << write(aFd, buf, b - buf);
 #endif
   va_end(ap);
 }
diff --git a/memory/replace/logalloc/moz.build b/memory/replace/logalloc/moz.build
index 16b67ad72aa..6f03f2861d7 100644
--- a/memory/replace/logalloc/moz.build
+++ b/memory/replace/logalloc/moz.build
@@ -38,6 +38,3 @@ if CONFIG['OS_TARGET'] == 'Android':
 DIRS += [
     'replay',
 ]
-
-# XXX: We should fix these warnings
-ALLOW_COMPILER_WARNINGS = True
diff --git a/memory/replace/logalloc/replay/moz.build b/memory/replace/logalloc/replay/moz.build
index 537c2092d9a..8f862c4f3a1 100644
--- a/memory/replace/logalloc/replay/moz.build
+++ b/memory/replace/logalloc/replay/moz.build
@@ -21,6 +21,3 @@ USE_LIBS += [
 ]
 
 DISABLE_STL_WRAPPING = True
-
-# XXX: We should fix these warnings
-ALLOW_COMPILER_WARNINGS = True

From d42bc1b3cda9871d82767271c574b932761f44ad Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 73/85] Bug 1185388 - Limit the number of cores used by WMF in
 ClearKey CDM - r=cpearce

---
 media/gmp-clearkey/0.1/VideoDecoder.cpp   |  2 +-
 media/gmp-clearkey/0.1/WMFH264Decoder.cpp | 10 +++++++++-
 media/gmp-clearkey/0.1/WMFH264Decoder.h   |  2 +-
 media/gmp-clearkey/0.1/WMFUtils.cpp       |  7 +++++++
 media/gmp-clearkey/0.1/WMFUtils.h         |  4 ++++
 5 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/media/gmp-clearkey/0.1/VideoDecoder.cpp b/media/gmp-clearkey/0.1/VideoDecoder.cpp
index 6dc65269593..8259ace8f4f 100644
--- a/media/gmp-clearkey/0.1/VideoDecoder.cpp
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -57,7 +57,7 @@ VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
   mCallback = aCallback;
   assert(mCallback);
   mDecoder = new WMFH264Decoder();
-  HRESULT hr = mDecoder->Init();
+  HRESULT hr = mDecoder->Init(aCoreCount);
   if (FAILED(hr)) {
     CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder");
     mCallback->Error(GMPGenericErr);
diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
index c0397419e21..ad0b5792377 100644
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -16,6 +16,7 @@
 
 #include "WMFH264Decoder.h"
 #include 
+#include 
 
 namespace wmf {
 
@@ -31,7 +32,7 @@ WMFH264Decoder::~WMFH264Decoder()
 }
 
 HRESULT
-WMFH264Decoder::Init()
+WMFH264Decoder::Init(int32_t aCoreCount)
 {
   HRESULT hr;
 
@@ -47,6 +48,13 @@ WMFH264Decoder::Init()
   }
   ENSURE(SUCCEEDED(hr), hr);
 
+  CComPtr attr;
+  hr = mDecoder->GetAttributes(&attr);
+  ENSURE(SUCCEEDED(hr), hr);
+  hr = attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
+                       GetNumThreads(aCoreCount));
+  ENSURE(SUCCEEDED(hr), hr);
+
   hr = SetDecoderInputType();
   ENSURE(SUCCEEDED(hr), hr);
 
diff --git a/media/gmp-clearkey/0.1/WMFH264Decoder.h b/media/gmp-clearkey/0.1/WMFH264Decoder.h
index 2d2b39659e4..91b7e046ffb 100644
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.h
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h
@@ -26,7 +26,7 @@ public:
   WMFH264Decoder();
   ~WMFH264Decoder();
 
-  HRESULT Init();
+  HRESULT Init(int32_t aCoreCount);
 
   HRESULT Input(const uint8_t* aData,
                 uint32_t aDataSize,
diff --git a/media/gmp-clearkey/0.1/WMFUtils.cpp b/media/gmp-clearkey/0.1/WMFUtils.cpp
index afa65197337..1dd93407eef 100644
--- a/media/gmp-clearkey/0.1/WMFUtils.cpp
+++ b/media/gmp-clearkey/0.1/WMFUtils.cpp
@@ -18,6 +18,7 @@
 #include "ClearKeyUtils.h"
 #include 
 
+#include 
 #include 
 
 #define INITGUID
@@ -253,4 +254,10 @@ CreateMFT(const CLSID& clsid,
   return S_OK;
 }
 
+int32_t
+GetNumThreads(int32_t aCoreCount)
+{
+  return aCoreCount > 4 ? -1 : (std::max)(aCoreCount - 1, 1);
+}
+
 } // namespace
diff --git a/media/gmp-clearkey/0.1/WMFUtils.h b/media/gmp-clearkey/0.1/WMFUtils.h
index 517c9718aa6..07e0d22638c 100644
--- a/media/gmp-clearkey/0.1/WMFUtils.h
+++ b/media/gmp-clearkey/0.1/WMFUtils.h
@@ -259,6 +259,10 @@ enum CodecType {
 // the given windows version we're running on.
 const char* WMFDecoderDllNameFor(CodecType aCodec);
 
+// Returns the maximum number of threads we want WMF to use for decoding
+// given the number of logical processors available.
+int32_t GetNumThreads(int32_t aCoreCount);
+
 } // namespace wmf
 
 #endif // __WMFUtils_h__

From 5c14fc51e32f009a9206d4141e5fbfb25128f21e Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 74/85] Bug 1172396 - Use GMPVideoDecoderTrialCreator when e10s
 is enabled - r=cpearce

---
 dom/media/eme/GMPVideoDecoderTrialCreator.cpp | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
index 23b06153aa6..62ac64b2a68 100644
--- a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
+++ b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
@@ -498,12 +498,6 @@ GMPVideoDecoderTrialCreator::MaybeAwaitTrialCreate(const nsAString& aKeySystem,
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    // Currently broken with e10s...
-    aPromisey->Resolve();
-    return;
-  }
-
   if (!mTestCreate.Contains(aKeySystem)) {
     mTestCreate.Put(aKeySystem, new TrialCreateData(aKeySystem));
   }

From 0df1cb967d8687c2bdf08dd6a6fac9a96fbbeed0 Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 75/85] Bug 1172396 - Use a fixed node ID for GMP trial
 creation - r=cpearce

---
 dom/media/eme/GMPVideoDecoderTrialCreator.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
index 62ac64b2a68..2f6358275d1 100644
--- a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
+++ b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
@@ -484,9 +484,9 @@ TestGMPVideoDecoder::CreateGMPVideoDecoder()
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
   UniquePtr callback(new Callback(this));
-  nsCString fakeNodeId;
-  if (NS_FAILED(GenerateRandomName(fakeNodeId, 32)) ||
-      NS_FAILED(mGMPService->GetGMPVideoDecoder(&tags, fakeNodeId, Move(callback)))) {
+  if (NS_FAILED(mGMPService->GetGMPVideoDecoder(&tags,
+                                                NS_LITERAL_CSTRING("fakeNodeId1234567890fakeNodeId12"),
+                                                Move(callback)))) {
     ReportFailure(NS_LITERAL_CSTRING("TestGMPVideoDecoder GMPService GetGMPVideoDecoder returned failure"));
   }
 }

From 467d4b55c2ac64fcb033cd4119be7ac0192789be Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 76/85] Bug 1172396 - Make GMP initialisation synchronous -
 r=cpearce

---
 dom/media/gmp/GMPChild.cpp  | 2 +-
 dom/media/gmp/GMPChild.h    | 2 +-
 dom/media/gmp/GMPParent.cpp | 7 ++-----
 dom/media/gmp/PGMP.ipdl     | 2 +-
 4 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp
index a71d5365a80..32c0da0593e 100644
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -433,7 +433,7 @@ GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
 }
 
 bool
-GMPChild::RecvStartPlugin()
+GMPChild::AnswerStartPlugin()
 {
   LOGD("%s", __FUNCTION__);
 
diff --git a/dom/media/gmp/GMPChild.h b/dom/media/gmp/GMPChild.h
index 7690b09f584..bb75939125b 100644
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -56,7 +56,7 @@ private:
   bool GetUTF8LibPath(nsACString& aOutLibPath);
 
   virtual bool RecvSetNodeId(const nsCString& aNodeId) override;
-  virtual bool RecvStartPlugin() override;
+  virtual bool AnswerStartPlugin() override;
 
   virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) override;
   virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) override;
diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp
index 187d44774fb..1e027f0cdbb 100644
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -164,17 +164,14 @@ GMPParent::LoadProcess()
     bool ok = SendSetNodeId(mNodeId);
     if (!ok) {
       LOGD("%s: Failed to send node id to child process", __FUNCTION__);
-      mProcess->Delete();
-      mProcess = nullptr;
       return NS_ERROR_FAILURE;
     }
     LOGD("%s: Sent node id to child process", __FUNCTION__);
 
-    ok = SendStartPlugin();
+    // Intr call to block initialization on plugin load.
+    ok = CallStartPlugin();
     if (!ok) {
       LOGD("%s: Failed to send start to child process", __FUNCTION__);
-      mProcess->Delete();
-      mProcess = nullptr;
       return NS_ERROR_FAILURE;
     }
     LOGD("%s: Sent StartPlugin to child process", __FUNCTION__);
diff --git a/dom/media/gmp/PGMP.ipdl b/dom/media/gmp/PGMP.ipdl
index 8a5a229ec5e..23513924a49 100644
--- a/dom/media/gmp/PGMP.ipdl
+++ b/dom/media/gmp/PGMP.ipdl
@@ -34,7 +34,7 @@ parent:
 child:
   async BeginAsyncShutdown();
   async CrashPluginNow();
-  async StartPlugin();
+  intr StartPlugin();
   async SetNodeId(nsCString nodeId);
   async CloseActive();
 };

From 498e05de0df1cbd6d4a5cd49606677c7f0857f9d Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 77/85] Bug 1172396 - Update GMP trial creation pref from
 chrome process - r=cpearce

---
 dom/media/eme/GMPVideoDecoderTrialCreator.cpp | 42 +++++++++++++++----
 dom/media/eme/GMPVideoDecoderTrialCreator.h   |  6 ++-
 dom/media/gmp/GMPServiceChild.cpp             | 34 +++++++++++++++
 dom/media/gmp/GMPServiceChild.h               |  2 +
 dom/media/gmp/GMPServiceParent.cpp            | 21 ++++++++++
 dom/media/gmp/GMPServiceParent.h              |  4 ++
 dom/media/gmp/PGMPService.ipdl                |  2 +
 dom/media/gmp/mozIGeckoMediaPluginService.idl |  8 +++-
 8 files changed, 109 insertions(+), 10 deletions(-)

diff --git a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
index 2f6358275d1..8231d74a69b 100644
--- a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
+++ b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
@@ -77,6 +77,36 @@ GMPVideoDecoderTrialCreator::GetCreateTrialState(const nsAString& aKeySystem)
   }
 }
 
+/* static */ void
+GMPVideoDecoderTrialCreator::UpdateTrialCreateState(const nsAString& aKeySystem,
+                                                    uint32_t aState)
+{
+  UpdateTrialCreateState(aKeySystem, (TrialCreateState)aState);
+}
+
+/* static */ void
+GMPVideoDecoderTrialCreator::UpdateTrialCreateState(const nsAString& aKeySystem,
+                                                    TrialCreateState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    // Pref has to be set from the chrome process. Dispatch to chrome via
+    // GMPService.
+    nsCOMPtr service =
+      do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+    NS_ENSURE_TRUE_VOID(service);
+
+    service->UpdateTrialCreateState(aKeySystem, (uint32_t)aState);
+    return;
+  }
+
+  const char* pref = TrialCreatePrefName(aKeySystem);
+  if (pref) {
+    Preferences::SetInt(pref, (int)aState);
+  }
+}
+
 void
 GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderFailed(const nsAString& aKeySystem,
                                                               const nsACString& aReason)
@@ -91,10 +121,8 @@ GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderFailed(const nsAString& a
     return;
   }
   data->mStatus = Failed;
-  const char* pref = TrialCreatePrefName(aKeySystem);
-  if (pref) {
-    Preferences::SetInt(pref, (int)Failed);
-  }
+  UpdateTrialCreateState(aKeySystem, Failed);
+
   for (nsRefPtr& promise: data->mPending) {
     promise->Reject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, aReason);
   }
@@ -115,10 +143,8 @@ GMPVideoDecoderTrialCreator::TrialCreateGMPVideoDecoderSucceeded(const nsAString
     return;
   }
   data->mStatus = Succeeded;
-  const char* pref = TrialCreatePrefName(aKeySystem);
-  if (pref) {
-    Preferences::SetInt(pref, (int)Succeeded);
-  }
+  UpdateTrialCreateState(aKeySystem, Succeeded);
+
   for (nsRefPtr& promise : data->mPending) {
     promise->Resolve();
   }
diff --git a/dom/media/eme/GMPVideoDecoderTrialCreator.h b/dom/media/eme/GMPVideoDecoderTrialCreator.h
index c31f417c842..273ba087d0d 100644
--- a/dom/media/eme/GMPVideoDecoderTrialCreator.h
+++ b/dom/media/eme/GMPVideoDecoderTrialCreator.h
@@ -39,6 +39,8 @@ public:
     MaybeAwaitTrialCreate(aKeySystem, p, aParent);
   }
 
+  static void UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState);
+
 private:
 
   class AbstractPromiseLike {
@@ -88,6 +90,8 @@ private:
   };
 
   static TrialCreateState GetCreateTrialState(const nsAString& aKeySystem);
+  static void UpdateTrialCreateState(const nsAString& aKeySystem,
+                                     TrialCreateState aState);
 
   struct TrialCreateData {
     explicit TrialCreateData(const nsAString& aKeySystem)
@@ -172,4 +176,4 @@ private:
 } // namespace dom
 } // namespace mozilla
 
-#endif
\ No newline at end of file
+#endif
diff --git a/dom/media/gmp/GMPServiceChild.cpp b/dom/media/gmp/GMPServiceChild.cpp
index cb3f5d31d23..0cdc2c0b820 100644
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -181,6 +181,40 @@ GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
   return NS_OK;
 }
 
+NS_IMETHODIMP
+GeckoMediaPluginServiceChild::UpdateTrialCreateState(const nsAString& aKeySystem,
+                                                     uint32_t aState)
+{
+  if (NS_GetCurrentThread() != mGMPThread) {
+    mGMPThread->Dispatch(NS_NewRunnableMethodWithArgs(
+      this, &GeckoMediaPluginServiceChild::UpdateTrialCreateState,
+      aKeySystem, aState), NS_DISPATCH_NORMAL);
+    return NS_OK;
+  }
+
+  class Callback : public GetServiceChildCallback
+  {
+  public:
+    Callback(const nsAString& aKeySystem, uint32_t aState)
+      : mKeySystem(aKeySystem)
+      , mState(aState)
+    { }
+
+    virtual void Done(GMPServiceChild* aService) override
+    {
+      aService->SendUpdateGMPTrialCreateState(mKeySystem, mState);
+    }
+
+  private:
+    nsString mKeySystem;
+    uint32_t mState;
+  };
+
+  UniquePtr callback(new Callback(aKeySystem, aState));
+  GetServiceChild(Move(callback));
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::Observe(nsISupports* aSubject,
                                       const char* aTopic,
diff --git a/dom/media/gmp/GMPServiceChild.h b/dom/media/gmp/GMPServiceChild.h
index 09946dd8b61..8f529cb1d26 100644
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -49,6 +49,8 @@ public:
                        const nsAString& aTopLevelOrigin,
                        bool aInPrivateBrowsingMode,
                        UniquePtr&& aCallback) override;
+  NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
+                                    uint32_t aState) override;
 
   NS_DECL_NSIOBSERVER
 
diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp
index 3830030edf1..2e08d645cee 100644
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -9,6 +9,7 @@
 #include "mozilla/Logging.h"
 #include "GMPParent.h"
 #include "GMPVideoDecoderParent.h"
+#include "mozilla/dom/GMPVideoDecoderTrialCreator.h"
 #include "nsIObserverService.h"
 #include "GeckoChildProcessHost.h"
 #include "mozilla/Preferences.h"
@@ -1202,6 +1203,18 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
   return rv;
 }
 
+NS_IMETHODIMP
+GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem,
+                                                      uint32_t aState)
+{
+  nsString keySystem(aKeySystem);
+  NS_DispatchToMainThread(NS_NewRunnableFunction([keySystem, aState] {
+    mozilla::dom::GMPVideoDecoderTrialCreator::UpdateTrialCreateState(keySystem, aState);
+  }));
+
+  return NS_OK;
+}
+
 static bool
 ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
 {
@@ -1571,6 +1584,14 @@ GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
   return NS_SUCCEEDED(rv);
 }
 
+bool
+GMPServiceParent::RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
+                                                const uint32_t& aState)
+{
+  mService->UpdateTrialCreateState(aKeySystem, aState);
+  return true;
+}
+
 /* static */
 bool
 GMPServiceParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
diff --git a/dom/media/gmp/GMPServiceParent.h b/dom/media/gmp/GMPServiceParent.h
index f08ca789496..7cfcf01c94b 100644
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -43,6 +43,8 @@ public:
                        const nsAString& aTopLevelOrigin,
                        bool aInPrivateBrowsingMode,
                        UniquePtr&& aCallback) override;
+  NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
+                                    uint32_t aState) override;
 
   NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
   NS_DECL_NSIOBSERVER
@@ -219,6 +221,8 @@ public:
                                             nsTArray&& aTags,
                                             bool* aHasPlugin,
                                             nsCString* aVersion);
+  virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
+                                             const uint32_t& aState) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
diff --git a/dom/media/gmp/PGMPService.ipdl b/dom/media/gmp/PGMPService.ipdl
index bdfc40be09a..440ee4638e6 100644
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -21,6 +21,8 @@ parent:
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
                     bool inPrivateBrowsing)
     returns (nsCString id);
+
+  async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status);
 };
 
 } // namespace gmp
diff --git a/dom/media/gmp/mozIGeckoMediaPluginService.idl b/dom/media/gmp/mozIGeckoMediaPluginService.idl
index e7541aad288..4bbd3cc2ba3 100644
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -52,7 +52,7 @@ native GetGMPVideoDecoderCallback(mozilla::UniquePtr
 native GetGMPVideoEncoderCallback(mozilla::UniquePtr&&);
 native GetNodeIdCallback(mozilla::UniquePtr&&);
 
-[scriptable, uuid(787cf744-eea8-48c8-b692-376e0485ef97)]
+[scriptable, uuid(661492d6-726b-4ba0-8e6e-14bfaf2b62e4)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
@@ -147,4 +147,10 @@ interface mozIGeckoMediaPluginService : nsISupports
                  in AString topLevelOrigin,
                  in bool inPrivateBrowsingMode,
                  in GetNodeIdCallback callback);
+
+  /**
+   * Stores the result of trying to create a decoder for the given keysystem.
+   */
+  [noscript]
+  void updateTrialCreateState(in AString keySystem, in uint32_t status);
 };

From 1dc30ef34345bc30f96ad5e375f55bc20a5e59ac Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 78/85] Bug 1172396 - Check for GMP resources from chrome
 process instead of content - r=cpearce

---
 dom/ipc/ContentParent.cpp              | 14 ++++
 dom/ipc/ContentParent.h                |  4 ++
 dom/ipc/PContent.ipdl                  |  2 +
 dom/media/eme/MediaKeySystemAccess.cpp | 91 +++++++++++++++++---------
 dom/media/eme/MediaKeySystemAccess.h   |  4 ++
 dom/media/eme/moz.build                |  2 +
 6 files changed, 85 insertions(+), 32 deletions(-)

diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index daf9a6f0010..ef6b34250ce 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -42,6 +42,7 @@
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
+#include "mozilla/dom/MediaKeySystemAccess.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
@@ -1098,6 +1099,19 @@ ContentParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
                                                            aVersion);
 }
 
+bool
+ContentParent::RecvIsGMPPresentOnDisk(const nsString& aKeySystem,
+                                      const nsCString& aVersion,
+                                      bool* aIsPresent,
+                                      nsCString* aMessage)
+{
+    *aIsPresent = MediaKeySystemAccess::IsGMPPresentOnDisk(aKeySystem,
+                                                           aVersion,
+                                                           *aMessage);
+
+    return true;
+}
+
 bool
 ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID)
 {
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
index 49df5b4f586..ff0525fec03 100644
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -176,6 +176,10 @@ public:
                                                nsTArray&& aTags,
                                                bool* aHasPlugin,
                                                nsCString* aVersion) override;
+    virtual bool RecvIsGMPPresentOnDisk(const nsString& aKeySystem,
+                                        const nsCString& aVersion,
+                                        bool* aIsPresent,
+                                        nsCString* aMessage) override;
 
     virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) override;
     virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) override;
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
index e5fd6767b33..080385f4341 100644
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -696,6 +696,8 @@ parent:
     async CreateGMPService();
     sync GetGMPPluginVersionForAPI(nsCString api, nsCString[] tags)
         returns (bool hasPlugin, nsCString version);
+    sync IsGMPPresentOnDisk(nsString keySystem, nsCString version)
+        returns (bool isPresent, nsCString message);
 
     /**
      * This call connects the content process to a plugin process. While this
diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
index 4685604af21..0c81c932799 100644
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -4,6 +4,7 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MediaKeySystemAccess.h"
 #include "mozilla/dom/MediaKeySystemAccessBinding.h"
 #include "mozilla/Preferences.h"
@@ -110,10 +111,7 @@ static bool
 AdobePluginFileExists(const nsACString& aVersionStr,
                       const nsAString& aFilename)
 {
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    NS_WARNING("AdobePluginFileExists() lying because it doesn't work with e10s");
-    return true;
-  }
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
   nsCOMPtr path;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(path));
@@ -153,6 +151,61 @@ AdobePluginVoucherExists(const nsACString& aVersionStr)
 }
 #endif
 
+/* static */ bool
+MediaKeySystemAccess::IsGMPPresentOnDisk(const nsAString& aKeySystem,
+                                         const nsACString& aVersion,
+                                         nsACString& aOutMessage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    // We need to be able to access the filesystem, so call this in the
+    // main process via ContentChild.
+    ContentChild* contentChild = ContentChild::GetSingleton();
+    if (NS_WARN_IF(!contentChild)) {
+      return false;
+    }
+
+    nsCString message;
+    bool result = false;
+    bool ok = contentChild->SendIsGMPPresentOnDisk(nsString(aKeySystem), nsCString(aVersion),
+                                                   &result, &message);
+    aOutMessage = message;
+    return ok && result;
+  }
+
+  bool isPresent = true;
+
+#if XP_WIN
+  if (aKeySystem.EqualsLiteral("com.adobe.primetime")) {
+    if (!AdobePluginDLLExists(aVersion)) {
+      NS_WARNING("Adobe EME plugin disappeared from disk!");
+      aOutMessage = NS_LITERAL_CSTRING("Adobe DLL was expected to be on disk but was not");
+      isPresent = false;
+    }
+    if (!AdobePluginVoucherExists(aVersion)) {
+      NS_WARNING("Adobe EME voucher disappeared from disk!");
+      aOutMessage = NS_LITERAL_CSTRING("Adobe plugin voucher was expected to be on disk but was not");
+      isPresent = false;
+    }
+
+    if (!isPresent) {
+      // Reset the prefs that Firefox's GMP downloader sets, so that
+      // Firefox will try to download the plugin next time the updater runs.
+      Preferences::ClearUser("media.gmp-eme-adobe.lastUpdate");
+      Preferences::ClearUser("media.gmp-eme-adobe.version");
+    } else if (!EMEVoucherFileExists()) {
+      // Gecko doesn't have a voucher file for the plugin-container.
+      // Adobe EME isn't going to work, so don't advertise that it will.
+      aOutMessage = NS_LITERAL_CSTRING("Plugin-container voucher not present");
+      isPresent = false;
+    }
+  }
+#endif
+
+  return isPresent;
+}
+
 static MediaKeySystemStatus
 EnsureMinCDMVersion(mozIGeckoMediaPluginService* aGMPService,
                     const nsAString& aKeySystem,
@@ -179,29 +232,9 @@ EnsureMinCDMVersion(mozIGeckoMediaPluginService* aGMPService,
     return MediaKeySystemStatus::Cdm_not_installed;
   }
 
-#ifdef XP_WIN
-  if (aKeySystem.EqualsLiteral("com.adobe.primetime")) {
-    // Verify that anti-virus hasn't "helpfully" deleted the Adobe GMP DLL,
-    // as we suspect may happen (Bug 1160382).
-    bool somethingMissing = false;
-    if (!AdobePluginDLLExists(versionStr)) {
-      aOutMessage = NS_LITERAL_CSTRING("Adobe DLL was expected to be on disk but was not");
-      somethingMissing = true;
-    }
-    if (!AdobePluginVoucherExists(versionStr)) {
-      aOutMessage = NS_LITERAL_CSTRING("Adobe plugin voucher was expected to be on disk but was not");
-      somethingMissing = true;
-    }
-    if (somethingMissing) {
-      NS_WARNING("Adobe EME plugin or voucher disappeared from disk!");
-      // Reset the prefs that Firefox's GMP downloader sets, so that
-      // Firefox will try to download the plugin next time the updater runs.
-      Preferences::ClearUser("media.gmp-eme-adobe.lastUpdate");
-      Preferences::ClearUser("media.gmp-eme-adobe.version");
-      return MediaKeySystemStatus::Cdm_not_installed;
-    }
+  if (!MediaKeySystemAccess::IsGMPPresentOnDisk(aKeySystem, versionStr, aOutMessage)) {
+    return MediaKeySystemStatus::Cdm_not_installed;
   }
-#endif
 
   nsresult rv;
   int32_t version = versionStr.ToInteger(&rv);
@@ -256,12 +289,6 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
       return MediaKeySystemStatus::Cdm_not_supported;
     }
 #endif
-    if (!EMEVoucherFileExists()) {
-      // Gecko doesn't have a voucher file for the plugin-container.
-      // Adobe EME isn't going to work, so don't advertise that it will.
-      aOutMessage = NS_LITERAL_CSTRING("Plugin-container voucher not present");
-      return MediaKeySystemStatus::Cdm_not_supported;
-    }
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, aOutMessage, aOutCdmVersion);
   }
 #endif
diff --git a/dom/media/eme/MediaKeySystemAccess.h b/dom/media/eme/MediaKeySystemAccess.h
index 5f5ed36f34d..6123bd10f46 100644
--- a/dom/media/eme/MediaKeySystemAccess.h
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -59,6 +59,10 @@ public:
                               const nsAString& aKeySystem,
                               MediaKeySystemStatus aStatus);
 
+  static bool IsGMPPresentOnDisk(const nsAString& aKeySystem,
+                                 const nsACString& aVersion,
+                                 nsACString& aOutMessage);
+
 private:
   nsCOMPtr mParent;
   const nsString mKeySystem;
diff --git a/dom/media/eme/moz.build b/dom/media/eme/moz.build
index e4d6be76c09..ca6ca01cd00 100644
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -41,4 +41,6 @@ UNIFIED_SOURCES += [
     'MediaKeySystemAccessManager.cpp',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'

From e47f0c7bf40f01172337abf30e79d1d8bff5cc5d Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:31:48 +1200
Subject: [PATCH 79/85] Bug 1190503 - Add telemetry to report details on GMP
 update failures - r=cpearce,vladan

---
 toolkit/components/telemetry/Histograms.json | 24 ++++++
 toolkit/modules/GMPInstallManager.jsm        | 46 ++++++++--
 toolkit/modules/GMPUtils.jsm                 | 88 ++++++++++++++++----
 3 files changed, 138 insertions(+), 20 deletions(-)

diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 40c4fae092c..eb60d5957ac 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -9061,4 +9061,28 @@
     "n_buckets": "20",
     "description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank."
   }
+  "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON": {
+    "alert_emails": ["edwin@mozilla.com"],
+    "expires_in_version": "50",
+    "kind": "enumerated",
+    "n_values": 10,
+    "releaseChannelCollection": "opt-out",
+    "description": "Reason for Adobe CDM failing to update. (1 = GMP_INVALID; 2 = GMP_HIDDEN; 3 = GMP_DISABLED; 4 = GMP_UPDATE_DISABLED)"
+  },
+  "VIDEO_EME_ADOBE_HIDDEN_REASON": {
+    "alert_emails": ["edwin@mozilla.com"],
+    "expires_in_version": "50",
+    "kind": "enumerated",
+    "n_values": 10,
+    "releaseChannelCollection": "opt-out",
+    "description": "Reason for Adobe CDM being hidden. (1 = UNSUPPORTED; 2 = EME_DISABLED)"
+  },
+  "VIDEO_EME_ADOBE_UNSUPPORTED_REASON": {
+    "alert_emails": ["edwin@mozilla.com"],
+    "expires_in_version": "50",
+    "kind": "enumerated",
+    "n_values": 10,
+    "releaseChannelCollection": "opt-out",
+    "description": "Reason for reporting the Adobe CDM to be unsupported. (1 = NOT_WINDOWS; 2 = WINDOWS_VERSION)"
+  },
 }
diff --git a/toolkit/modules/GMPInstallManager.jsm b/toolkit/modules/GMPInstallManager.jsm
index 0fdf9eed599..b6c83b3f299 100644
--- a/toolkit/modules/GMPInstallManager.jsm
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -15,6 +15,13 @@ const DOWNLOAD_INTERVAL  = 0;
 // 1 day default
 const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24;
 
+let GMPInstallFailureReason = {
+  GMP_INVALID: 1,
+  GMP_HIDDEN: 2,
+  GMP_DISABLED: 3,
+  GMP_UPDATE_DISABLED: 4,
+};
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
@@ -345,8 +352,11 @@ GMPInstallManager.prototype = {
   get _isEMEEnabled() {
     return GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true);
   },
+  _isAddonEnabled: function(aAddon) {
+    return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, aAddon);
+  },
   _isAddonUpdateEnabled: function(aAddon) {
-    return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, aAddon) &&
+    return this._isAddonEnabled(aAddon) &&
            GMPPrefs.get(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, aAddon);
   },
   _updateLastCheck: function() {
@@ -405,18 +415,42 @@ GMPInstallManager.prototype = {
       let addonsToInstall = gmpAddons.filter(function(gmpAddon) {
         log.info("Found addon: " + gmpAddon.toString());
 
-        if (!gmpAddon.isValid || GMPUtils.isPluginHidden(gmpAddon) ||
-            gmpAddon.isInstalled) {
-          log.info("Addon invalid, hidden or already installed.");
+        if (!gmpAddon.isValid) {
+          GMPUtils.maybeReportTelemetry(gmpAddon.id,
+                                        "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON",
+                                        GMPInstallFailureReason.GMP_INVALID);
+          log.info("Addon |" + gmpAddon.id + "| is invalid.");
+          return false;
+        }
+
+        if (GMPUtils.isPluginHidden(gmpAddon)) {
+          GMPUtils.maybeReportTelemetry(gmpAddon.id,
+                                        "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON",
+                                        GMPInstallFailureReason.GMP_HIDDEN);
+          log.info("Addon |" + gmpAddon.id + "| has been hidden.");
+          return false;
+        }
+
+        if (gmpAddon.isInstalled) {
+          log.info("Addon |" + gmpAddon.id + "| already installed.");
           return false;
         }
 
         let addonUpdateEnabled = false;
         if (GMP_PLUGIN_IDS.indexOf(gmpAddon.id) >= 0) {
-          addonUpdateEnabled = this._isAddonUpdateEnabled(gmpAddon.id);
-          if (!addonUpdateEnabled) {
+          if (!this._isAddonEnabled(gmpAddon.id)) {
+            GMPUtils.maybeReportTelemetry(gmpAddon.id,
+                                          "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON",
+                                          GMPInstallFailureReason.GMP_DISABLED);
+            log.info("GMP |" + gmpAddon.id + "| has been disabled; skipping check.");
+          } else if (!this._isAddonUpdateEnabled(gmpAddon.id)) {
+            GMPUtils.maybeReportTelemetry(gmpAddon.id,
+                                          "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON",
+                                          GMPInstallFailureReason.GMP_UPDATE_DISABLED);
             log.info("Auto-update is off for " + gmpAddon.id +
                      ", skipping check.");
+          } else {
+            addonUpdateEnabled = true;
           }
         } else {
           // Currently, we only support installs of OpenH264 and EME plugins.
diff --git a/toolkit/modules/GMPUtils.jsm b/toolkit/modules/GMPUtils.jsm
index 8ac75806f07..bc6d66dd927 100644
--- a/toolkit/modules/GMPUtils.jsm
+++ b/toolkit/modules/GMPUtils.jsm
@@ -21,6 +21,16 @@ const OPEN_H264_ID  = "gmp-gmpopenh264";
 const EME_ADOBE_ID  = "gmp-eme-adobe";
 const GMP_PLUGIN_IDS = [ OPEN_H264_ID, EME_ADOBE_ID ];
 
+let GMPPluginUnsupportedReason = {
+  NOT_WINDOWS: 1,
+  WINDOWS_VERSION: 2,
+};
+
+let GMPPluginHiddenReason = {
+  UNSUPPORTED: 1,
+  EME_DISABLED: 2,
+};
+
 this.GMPUtils = {
   /**
    * Checks whether or not a given plugin is hidden. Hidden plugins are neither
@@ -29,14 +39,25 @@ this.GMPUtils = {
    *          The plugin to check.
    */
   isPluginHidden: function(aPlugin) {
-    if (aPlugin.isEME) {
-      if (this._isPluginSupported(aPlugin) ||
-          this._isPluginForcedVisible(aPlugin)) {
-        return !GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true);
-      } else {
-        return true;
-      }
+    if (!aPlugin.isEME) {
+      return false;
     }
+
+    if (!this._isPluginSupported(aPlugin) &&
+        !this._isPluginForcedVisible(aPlugin)) {
+      this.maybeReportTelemetry(aPlugin.id,
+                                "VIDEO_EME_ADOBE_HIDDEN_REASON",
+                                GMPPluginHiddenReason.UNSUPPORTED);
+      return true;
+    }
+
+    if (!GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
+      this.maybeReportTelemetry(aPlugin.id,
+                                "VIDEO_EME_ADOBE_HIDDEN_REASON",
+                                GMPPluginHiddenReason.EME_DISABLED);
+      return true;
+    }
+
     return false;
   },
 
@@ -46,13 +67,27 @@ this.GMPUtils = {
    *          The plugin to check.
    */
   _isPluginSupported: function(aPlugin) {
-    if (aPlugin.id == EME_ADOBE_ID) {
-      if (Services.appinfo.OS == "WINNT") {
-        return Services.sysinfo.getPropertyAsInt32("version") >= 6;
-      } else {
-        return false;
-      }
+    if (aPlugin.id != EME_ADOBE_ID) {
+      // Only checking Adobe EME at the moment.
+      return true;
     }
+
+    if (Services.appinfo.OS != "WINNT") {
+      // Non-Windows OSes currently unsupported.
+      this.maybeReportTelemetry(aPlugin.id,
+                                "VIDEO_EME_ADOBE_UNSUPPORTED_REASON",
+                                GMPPluginUnsupportedReason.NOT_WINDOWS);
+      return false;
+    }
+
+    if (Services.sysinfo.getPropertyAsInt32("version") < 6) {
+      // Windows versions before Vista are unsupported.
+      this.maybeReportTelemetry(aPlugin.id,
+                                "VIDEO_EME_ADOBE_UNSUPPORTED_REASON",
+                                GMPPluginUnsupportedReason.WINDOWS_VERSION);
+      return false;
+    }
+
     return true;
   },
 
@@ -65,6 +100,31 @@ this.GMPUtils = {
   _isPluginForcedVisible: function(aPlugin) {
     return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_FORCEVISIBLE, false, aPlugin.id);
   },
+
+  /**
+   * Report telemetry value, but only for Adobe CDM and only once per key
+   * per session.
+   */
+  maybeReportTelemetry: function(aPluginId, key, value) {
+    if (aPluginId != EME_ADOBE_ID) {
+      // Only report for Adobe CDM.
+      return;
+    }
+
+    if (!this.reportedKeys) {
+      this.reportedKeys = [];
+    }
+    if (this.reportedKeys.indexOf(key) >= 0) {
+      // Only report each key once per session.
+      return;
+    }
+    this.reportedKeys.push(key);
+
+    let hist = Services.telemetry.getHistogramById(key);
+    if (hist) {
+      hist.add(value);
+    }
+  }
 };
 
 /**
@@ -154,4 +214,4 @@ this.GMPPrefs = {
   getPrefKey: function(aKey, aPlugin) {
     return aKey.replace("{0}", aPlugin || "");
   },
-};
\ No newline at end of file
+};

From 9a2a628533a3ca81a68a754159656f0722e9bef5 Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 17:43:12 +1200
Subject: [PATCH 80/85] Bug 1190503 - Fix Histograms.json syntax - r=bustage

---
 toolkit/components/telemetry/Histograms.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index eb60d5957ac..2b050f27c76 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -9060,7 +9060,7 @@
     "high": "100",
     "n_buckets": "20",
     "description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank."
-  }
+  },
   "VIDEO_EME_ADOBE_INSTALL_FAILED_REASON": {
     "alert_emails": ["edwin@mozilla.com"],
     "expires_in_version": "50",
@@ -9084,5 +9084,5 @@
     "n_values": 10,
     "releaseChannelCollection": "opt-out",
     "description": "Reason for reporting the Adobe CDM to be unsupported. (1 = NOT_WINDOWS; 2 = WINDOWS_VERSION)"
-  },
+  }
 }

From 8a0c89214f49bebad0cd70b2356510f9528793dd Mon Sep 17 00:00:00 2001
From: Aaron Klotz 
Date: Fri, 14 Aug 2015 16:27:03 -0600
Subject: [PATCH 81/85] Bug 1194890 - Ensure that any user32 imports to mozglue
 are delay loaded; r=glandium

---
 mozglue/build/moz.build | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build
index 0702be0dcb7..82bc737486f 100644
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -26,6 +26,10 @@ if CONFIG['MOZ_ASAN']:
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     DEFFILE = 'mozglue.def'
+    # We'll break the DLL blocklist if we immediately load user32.dll
+    DELAYLOAD_DLLS += [
+        'user32.dll',
+    ]
 
 if not CONFIG['JS_STANDALONE']:
 

From 1d57330ea04d8bfe8ed9f35b67a60a54b6c9fe8d Mon Sep 17 00:00:00 2001
From: Vaibhav Agrawal 
Date: Wed, 26 Aug 2015 16:51:15 -0700
Subject: [PATCH 82/85] Bug 999450 - Add find-test-chunk command in mach to
 discover the chunk for a mochitest on a platform. r=chmanchester

---
 testing/mach_commands.py           | 90 ++++++++++++++++++++++++++++++
 testing/mochitest/mach_commands.py | 54 +++++++++++-------
 2 files changed, 124 insertions(+), 20 deletions(-)

diff --git a/testing/mach_commands.py b/testing/mach_commands.py
index 3ac054dbead..1e89095aa5c 100644
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -4,8 +4,10 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+import json
 import os
 import sys
+import tempfile
 
 from mach.decorators import (
     CommandArgument,
@@ -14,6 +16,7 @@ from mach.decorators import (
 )
 
 from mozbuild.base import MachCommandBase
+from argparse import ArgumentParser
 
 
 UNKNOWN_TEST = '''
@@ -500,3 +503,90 @@ class PushToTry(MachCommandBase):
             at.push_to_try(msg, verbose)
 
         return
+
+
+def get_parser(argv=None):
+    parser = ArgumentParser()
+    parser.add_argument(dest="suite_name",
+                        nargs=1,
+                        choices=['mochitest'],
+                        type=str,
+                        help="The test for which chunk should be found. It corresponds "
+                             "to the mach test invoked (only 'mochitest' currently).")
+
+    parser.add_argument(dest="test_path",
+                        nargs=1,
+                        type=str,
+                        help="The test (any mochitest) for which chunk should be found.")
+
+    parser.add_argument('--total-chunks',
+                        type=int,
+                        dest='total_chunks',
+                        required=True,
+                        help='Total number of chunks to split tests into.',
+                        default=None
+                        )
+
+    parser.add_argument('-f', "--flavor",
+                        dest="flavor",
+                        type=str,
+                        help="Flavor to which the test belongs to.")
+
+    parser.add_argument('--chunk-by-runtime',
+                        action='store_true',
+                        dest='chunk_by_runtime',
+                        help='Group tests such that each chunk has roughly the same runtime.',
+                        default=False,
+                        )
+
+    parser.add_argument('--chunk-by-dir',
+                        type=int,
+                        dest='chunk_by_dir',
+                        help='Group tests together in the same chunk that are in the same top '
+                             'chunkByDir directories.',
+                        default=None,
+                        )
+
+    return parser
+
+
+@CommandProvider
+class ChunkFinder(MachCommandBase):
+    @Command('find-test-chunk', category='testing',
+             description='Find which chunk a test belongs to (works for mochitest).',
+             parser=get_parser)
+    def chunk_finder(self, **kwargs):
+        flavor = kwargs['flavor']
+        total_chunks = kwargs['total_chunks']
+        test_path = kwargs['test_path'][0]
+        suite_name = kwargs['suite_name'][0]
+        _, dump_tests = tempfile.mkstemp()
+        args = {
+            'totalChunks': total_chunks,
+            'dump_tests': dump_tests,
+            'chunkByDir': kwargs['chunk_by_dir'],
+            'chunkByRuntime': kwargs['chunk_by_runtime'],
+        }
+
+        found = False
+        for this_chunk in range(1, total_chunks+1):
+            args['thisChunk'] = this_chunk
+            try:
+                self._mach_context.commands.dispatch(suite_name, self._mach_context, flavor=flavor, resolve_tests=False, **args)
+            except SystemExit:
+                pass
+            except KeyboardInterrupt:
+                break
+
+            fp = open(os.path.expanduser(args['dump_tests']), 'r')
+            tests = json.loads(fp.read())['active_tests']
+            paths = [t['path'] for t in tests]
+            if test_path in paths:
+                print("The test %s is present in chunk number: %d (it may be skipped)." % (test_path, this_chunk))
+                found = True
+                break
+
+        if not found:
+            raise Exception("Test %s not found." % test_path)
+        # Clean up the file
+        os.remove(dump_tests)
diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py
index cdc777b7fb9..eb417591dea 100644
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -279,9 +279,10 @@ class MochitestRunner(MozbuildObject):
         options = Namespace(**kwargs)
 
         from manifestparser import TestManifest
-        manifest = TestManifest()
-        manifest.tests.extend(tests)
-        options.manifestFile = manifest
+        if tests:
+            manifest = TestManifest()
+            manifest.tests.extend(tests)
+            options.manifestFile = manifest
 
         if options.desktop:
             return mochitest.run_desktop_mochitests(options)
@@ -335,16 +336,17 @@ class MochitestRunner(MozbuildObject):
             options.xrePath = self.get_webapp_runtime_xre_path()
 
         from manifestparser import TestManifest
-        manifest = TestManifest()
-        manifest.tests.extend(tests)
-        options.manifestFile = manifest
+        if tests:
+            manifest = TestManifest()
+            manifest.tests.extend(tests)
+            options.manifestFile = manifest
 
-        # When developing mochitest-plain tests, it's often useful to be able to
-        # refresh the page to pick up modifications. Therefore leave the browser
-        # open if only running a single mochitest-plain test. This behaviour can
-        # be overridden by passing in --keep-open=false.
-        if len(tests) == 1 and options.keep_open is None and suite == 'plain':
-            options.keep_open = True
+            # When developing mochitest-plain tests, it's often useful to be able to
+            # refresh the page to pick up modifications. Therefore leave the browser
+            # open if only running a single mochitest-plain test. This behaviour can
+            # be overridden by passing in --keep-open=false.
+            if len(tests) == 1 and options.keep_open is None and suite == 'plain':
+                options.keep_open = True
 
         # We need this to enable colorization of output.
         self.log_manager.enable_unstructured()
@@ -367,9 +369,10 @@ class MochitestRunner(MozbuildObject):
         options = Namespace(**kwargs)
 
         from manifestparser import TestManifest
-        manifest = TestManifest()
-        manifest.tests.extend(tests)
-        options.manifestFile = manifest
+        if tests:
+            manifest = TestManifest()
+            manifest.tests.extend(tests)
+            options.manifestFile = manifest
 
         return runtestsremote.run_test_harness(options)
 
@@ -388,9 +391,10 @@ class MochitestRunner(MozbuildObject):
         options = Namespace(**kwargs)
 
         from manifestparser import TestManifest
-        manifest = TestManifest()
-        manifest.tests.extend(tests)
-        options.manifestFile = manifest
+        if tests:
+            manifest = TestManifest()
+            manifest.tests.extend(tests)
+            options.manifestFile = manifest
 
         return runrobocop.run_test_harness(options)
 
@@ -459,7 +463,7 @@ class MachCommands(MachCommandBase):
                      metavar='{{{}}}'.format(', '.join(CANONICAL_FLAVORS)),
                      choices=SUPPORTED_FLAVORS,
                      help='Only run tests of this flavor.')
-    def run_mochitest_general(self, flavor=None, test_objects=None, **kwargs):
+    def run_mochitest_general(self, flavor=None, test_objects=None, resolve_tests=True, **kwargs):
         buildapp = None
         for app in SUPPORTED_APPS:
             if is_buildapp_in(app)(self):
@@ -501,7 +505,9 @@ class MachCommands(MachCommandBase):
                 test_paths = new_paths
 
         mochitest = self._spawn(MochitestRunner)
-        tests = mochitest.resolve_tests(test_paths, test_objects, cwd=self._mach_context.cwd)
+        tests = []
+        if resolve_tests:
+            tests = mochitest.resolve_tests(test_paths, test_objects, cwd=self._mach_context.cwd)
 
         subsuite = kwargs.get('subsuite')
         if subsuite == 'default':
@@ -530,6 +536,14 @@ class MachCommands(MachCommandBase):
 
             suites[key].append(test)
 
+        # This is a hack to introduce an option in mach to not send
+        # filtered tests to the mochitest harness. Mochitest harness will read
+        # the master manifest in that case.
+        if not resolve_tests:
+            for flavor in flavors:
+                key = (flavor, kwargs.get('subsuite'))
+                suites[key] = []
+
         if not suites:
             # Make it very clear why no tests were found
             if not unsupported:

From a5a987b416f543291f11eaec3954e154c06f465d Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 18:06:57 +1200
Subject: [PATCH 83/85] Bug 1172396 - Fix for ContentParent on non-EME builds
 on a CLOSED TREE - r=bustage

---
 dom/ipc/ContentParent.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index ef6b34250ce..38a3416eb73 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -42,7 +42,9 @@
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
+#ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccess.h"
+#endif
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
@@ -1105,9 +1107,13 @@ ContentParent::RecvIsGMPPresentOnDisk(const nsString& aKeySystem,
                                       bool* aIsPresent,
                                       nsCString* aMessage)
 {
+#ifdef MOZ_EME
     *aIsPresent = MediaKeySystemAccess::IsGMPPresentOnDisk(aKeySystem,
                                                            aVersion,
                                                            *aMessage);
+#else
+    *aIsPresent = false;
+#endif
 
     return true;
 }

From 38b8fab61e24c44041fdb992bfafda794cf2be4b Mon Sep 17 00:00:00 2001
From: Edwin Flores 
Date: Tue, 1 Sep 2015 18:40:36 +1200
Subject: [PATCH 84/85] Bug 1172396 - Fix bustage... again. CLOSED TREE. -
 r=bustage

---
 dom/media/gmp/GMPServiceParent.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp
index 2e08d645cee..c705095c1d7 100644
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -9,7 +9,9 @@
 #include "mozilla/Logging.h"
 #include "GMPParent.h"
 #include "GMPVideoDecoderParent.h"
+#ifdef MOZ_EME
 #include "mozilla/dom/GMPVideoDecoderTrialCreator.h"
+#endif
 #include "nsIObserverService.h"
 #include "GeckoChildProcessHost.h"
 #include "mozilla/Preferences.h"
@@ -1207,12 +1209,16 @@ NS_IMETHODIMP
 GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem,
                                                       uint32_t aState)
 {
+#ifdef MOZ_EME
   nsString keySystem(aKeySystem);
   NS_DispatchToMainThread(NS_NewRunnableFunction([keySystem, aState] {
     mozilla::dom::GMPVideoDecoderTrialCreator::UpdateTrialCreateState(keySystem, aState);
   }));
 
   return NS_OK;
+#else
+  return NS_ERROR_FAILURE;
+#endif
 }
 
 static bool

From 07c500f77134550bca24ac7a0b97ffabd9ca9586 Mon Sep 17 00:00:00 2001
From: "Carsten \"Tomcat\" Book" 
Date: Tue, 1 Sep 2015 09:39:25 +0200
Subject: [PATCH 85/85] Backed out changeset e4e12583c280 (bug 1170894) for
 various mochitest testfailures on a CLOSED TREE

---
 dom/base/nsFrameLoader.cpp                    | 87 +------------------
 dom/base/nsFrameLoader.h                      |  2 -
 dom/base/nsIFrameLoader.idl                   | 10 +--
 dom/base/test/mochitest.ini                   |  2 -
 .../test/test_frameLoader_switchProcess.html  | 81 -----------------
 dom/browser-element/BrowserElementParent.js   | 48 ++++------
 dom/html/nsBrowserElement.cpp                 |  4 -
 dom/ipc/TabParent.cpp                         | 30 -------
 dom/ipc/TabParent.h                           |  4 -
 9 files changed, 19 insertions(+), 249 deletions(-)
 delete mode 100644 dom/base/test/test_frameLoader_switchProcess.html

diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp
index aa62828a9f8..7265c4df027 100644
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -276,42 +276,6 @@ nsFrameLoader::LoadURI(nsIURI* aURI)
   return rv;
 }
 
-NS_IMETHODIMP
-nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI)
-{
-  nsCOMPtr URIToLoad = aURI;
-  nsRefPtr tp = nullptr;
-
-  MutableTabContext context;
-  nsCOMPtr ownApp = GetOwnApp();
-  nsCOMPtr containingApp = GetContainingApp();
-
-  bool tabContextUpdated = true;
-  if (ownApp) {
-    tabContextUpdated = context.SetTabContextForAppFrame(ownApp, containingApp);
-  } else if (OwnerIsBrowserFrame()) {
-    // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp.
-    tabContextUpdated = context.SetTabContextForBrowserFrame(containingApp);
-  } else {
-    tabContextUpdated = context.SetTabContextForNormalFrame();
-  }
-  NS_ENSURE_STATE(tabContextUpdated);
-
-  nsCOMPtr ownerElement = mOwnerContent;
-  tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr);
-  if (!tp) {
-    return NS_ERROR_FAILURE;
-  }
-  mRemoteBrowserShown = false;
-
-  nsresult rv = SwapRemoteBrowser(tp);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  LoadURI(URIToLoad);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsFrameLoader::SetIsPrerendered()
 {
@@ -2632,58 +2596,9 @@ nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
   mRemoteFrame = true;
   mRemoteBrowser = TabParent::GetFrom(aTabParent);
   mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0;
-  ShowRemoteFrame(ScreenIntSize(0, 0));
-}
-
-nsresult
-nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent)
-{
-  nsRefPtr newParent = TabParent::GetFrom(aTabParent);
-  if (!newParent || !mRemoteBrowser) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-  if (!IsRemoteFrame()) {
-    NS_WARNING("Switching from in-process to out-of-process is not supported.");
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-  if (!OwnerIsBrowserOrAppFrame()) {
-    NS_WARNING("Switching process for non-mozbrowser/app frame is not supported.");
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-  if (newParent == mRemoteBrowser) {
-    return NS_OK;
-  }
-  nsCOMPtr os = services::GetObserverService();
-  if (os) {
-    os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                        "frameloader-message-manager-will-change", nullptr);
-  }
-
-  mRemoteBrowser->CacheFrameLoader(nullptr);
-  mRemoteBrowser->SetOwnerElement(nullptr);
-  mRemoteBrowser->Detach();
-  mRemoteBrowser->Destroy();
-
-  if (mMessageManager) {
-    mMessageManager->Disconnect();
-    mMessageManager = nullptr;
-  }
-
-  mRemoteBrowser = newParent;
-  mRemoteBrowser->Attach(this);
-  mChildID = mRemoteBrowser->Manager()->ChildID();
   ReallyLoadFrameScripts();
   InitializeBrowserAPI();
-
-  if (os) {
-    os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                        "frameloader-message-manager-changed", nullptr);
-  }
-  if (!mRemoteBrowserShown) {
-    ShowRemoteFrame(ScreenIntSize(0, 0));
-  }
-
-  return NS_OK;
+  ShowRemoteFrame(ScreenIntSize(0, 0));
 }
 
 void
diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h
index 6ba2b4ea9f3..6515d40bd9c 100644
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -181,8 +181,6 @@ public:
    */
   void SetRemoteBrowser(nsITabParent* aTabParent);
 
-  nsresult SwapRemoteBrowser(nsITabParent* aTabParent);
-
   /**
    * Stashes a detached view on the frame loader. We do this when we're
    * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is
diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl
index f993b8f07ae..0d571309023 100644
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -16,7 +16,7 @@ interface nsIDOMElement;
 interface nsITabParent;
 interface nsILoadContext;
 
-[scriptable, builtinclass, uuid(c6e00815-b7a1-4544-b309-a85b86cb1747)]
+[scriptable, builtinclass, uuid(d24f9330-ae4e-11e4-ab27-0800200c9a66)]
 interface nsIFrameLoader : nsISupports
 {
   /**
@@ -49,14 +49,6 @@ interface nsIFrameLoader : nsISupports
    */
   void loadURI(in nsIURI aURI);
 
-  /**
-   * Loads the specified URI in this frame but using a different process.
-   * Behaves identically to loadURI, except that this method only works
-   * with remote frame.
-   * Throws an exception with non-remote frames.
-   */
-  void switchProcessAndLoadURI(in nsIURI aURI);
-
   /**
    * Puts the frameloader in prerendering mode.
    */
diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
index 47bbc6ca575..e8023134bc8 100644
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -840,5 +840,3 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_postMessages.html]
 support-files = worker_postMessages.js
 [test_window_proto.html]
-[test_frameLoader_switchProcess.html]
-skip-if = e10s || os != 'linux' || buildapp != 'browser'
diff --git a/dom/base/test/test_frameLoader_switchProcess.html b/dom/base/test/test_frameLoader_switchProcess.html
deleted file mode 100644
index bf1029c0d65..00000000000
--- a/dom/base/test/test_frameLoader_switchProcess.html
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-  Test frameLoader SwitchProcessAndLoadURI
-  
-  
-
-
-
-
-
-
diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js
index ee573e4b57d..4856bcfd338 100644
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -77,8 +77,6 @@ function BrowserElementParent() {
   Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true);
-  Services.obs.addObserver(this, 'frameloader-message-manager-will-change', /* ownsWeak = */ true);
-  Services.obs.addObserver(this, 'frameloader-message-manager-changed', /* ownsWeak = */ true);
 }
 
 BrowserElementParent.prototype = {
@@ -163,17 +161,10 @@ BrowserElementParent.prototype = {
 
   _setupMessageListener: function() {
     this._mm = this._frameLoader.messageManager;
-    this._isWidget = this._frameLoader
-                         .QueryInterface(Ci.nsIFrameLoader)
-                         .ownerIsWidget;
-    this._mm.addMessageListener('browser-element-api:call', this);
-    this._mm.loadFrameScript("chrome://global/content/extensions.js", true);
-  },
-
-  receiveMessage: function(aMsg) {
-    if (!this._isAlive()) {
-      return;
-    }
+    let self = this;
+    let isWidget = this._frameLoader
+                       .QueryInterface(Ci.nsIFrameLoader)
+                       .ownerIsWidget;
 
     // Messages we receive are handed to functions which take a (data) argument,
     // where |data| is the message manager's data object.
@@ -231,15 +222,20 @@ BrowserElementParent.prototype = {
       "opentab": this._fireEventFromMsg
     };
 
-    if (aMsg.data.msg_name in mmCalls) {
-      mmCalls[aMsg.data.msg_name].apply(this, arguments);
-    } else if (!this._isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
-      mmSecuritySensitiveCalls[aMsg.data.msg_name].apply(this, arguments);
-    }
-  },
+    this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
+      if (!self._isAlive()) {
+        return;
+      }
 
-  _removeMessageListener: function() {
-    this._mm.removeMessageListener('browser-element-api:call', this);
+      if (aMsg.data.msg_name in mmCalls) {
+        return mmCalls[aMsg.data.msg_name].apply(self, arguments);
+      } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
+        return mmSecuritySensitiveCalls[aMsg.data.msg_name]
+                 .apply(self, arguments);
+      }
+    });
+
+    this._mm.loadFrameScript("chrome://global/content/extensions.js", true);
   },
 
   /**
@@ -1109,16 +1105,6 @@ BrowserElementParent.prototype = {
         this._sendAsyncMsg('copypaste-do-command', { command: data });
       }
       break;
-    case 'frameloader-message-manager-will-change':
-      if (this._isAlive() && subject == this._frameLoader) {
-        this._removeMessageListener();
-      }
-      break;
-    case 'frameloader-message-manager-changed':
-      if (this._isAlive() && subject == this._frameLoader) {
-        this._setupMessageListener();
-      }
-      break;
     default:
       debug('Unknown topic: ' + topic);
       break;
diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp
index 9920825d513..0bf2a46e72b 100644
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -54,10 +54,6 @@ nsBrowserElement::IsNotWidgetOrThrow(ErrorResult& aRv)
 void
 nsBrowserElement::InitBrowserElementAPI()
 {
-  if (mBrowserElementAPI) {
-    return;
-  }
-
   bool isBrowserOrApp;
   nsCOMPtr frameLoader = GetFrameLoader();
   NS_ENSURE_TRUE_VOID(frameLoader);
diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp
index 03e2acad953..c4c82df953d 100644
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -278,7 +278,6 @@ TabParent::TabParent(nsIContentParent* aManager,
   , mManager(aManager)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
-  , mIsDetached(true)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
@@ -473,35 +472,6 @@ TabParent::Destroy()
   mMarkedDestroying = true;
 }
 
-void
-TabParent::Detach()
-{
-  if (mIsDetached) {
-    return;
-  }
-  RemoveWindowListeners();
-  if (RenderFrameParent* frame = GetRenderFrame()) {
-    RemoveTabParentFromTable(frame->GetLayersId());
-  }
-  mIsDetached = true;
-}
-
-void
-TabParent::Attach(nsFrameLoader* aFrameLoader)
-{
-  MOZ_ASSERT(mIsDetached);
-  if (!mIsDetached) {
-    return;
-  }
-  Element* ownerElement = aFrameLoader->GetOwnerContent();
-  SetOwnerElement(ownerElement);
-  if (RenderFrameParent* frame = GetRenderFrame()) {
-    AddTabParentToTable(frame->GetLayersId(), this);
-    frame->OwnerContentChanged(ownerElement);
-  }
-  mIsDetached = false;
-}
-
 bool
 TabParent::Recv__delete__()
 {
diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h
index b20784924bd..c8b61d1cbef 100644
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -122,8 +122,6 @@ public:
     nsIXULBrowserWindow* GetXULBrowserWindow();
 
     void Destroy();
-    void Detach();
-    void Attach(nsFrameLoader* aFrameLoader);
 
     void RemoveWindowListeners();
     void AddWindowListeners();
@@ -517,8 +515,6 @@ private:
     bool mMarkedDestroying;
     // When true, the TabParent is invalid and we should not send IPC messages anymore.
     bool mIsDestroyed;
-    // When true, the TabParent is detached from the frame loader.
-    bool mIsDetached;
     // Whether we have already sent a FileDescriptor for the app package.
     bool mAppPackageFileDescriptorSent;