From 8e434d1c102a5a6ea10461decb7b3ea4677f89ec Mon Sep 17 00:00:00 2001 From: Sotaro Ikeda Date: Sun, 13 Mar 2016 19:46:23 -0700 Subject: [PATCH 01/24] Bug 1255320 - Create DrawTarget with DIB as similar DrawTarget r=jrmuizel --- gfx/2d/DrawTargetCairo.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index ad4b4f16e16d..be05c5c6dc72 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -1749,9 +1749,18 @@ DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFo } } - cairo_surface_t* similar = cairo_surface_create_similar(mSurface, - GfxFormatToCairoContent(aFormat), - aSize.width, aSize.height); + cairo_surface_t* similar; +#ifdef CAIRO_HAS_WIN32_SURFACE + if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_WIN32) { + similar = cairo_win32_surface_create_with_dib(GfxFormatToCairoFormat(aFormat), + aSize.width, aSize.height); + } else +#endif + { + similar = cairo_surface_create_similar(mSurface, + GfxFormatToCairoContent(aFormat), + aSize.width, aSize.height); + } if (!cairo_surface_status(similar)) { RefPtr target = new DrawTargetCairo(); From e112dca69276c4487997af0f1bfd6594830da222 Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Sat, 12 Mar 2016 19:53:50 +0800 Subject: [PATCH 02/24] Bug 1225412 Part 1 - Make AsyncEventDispatcher cancelable. r=smaug Also I found that mBubbles are not initialized in the constructor in AsyncEventDispatcher.cpp. I initialized those bool member variables directly. MozReview-Commit-ID: FiU4NKGJjU9 --HG-- extra : rebase_source : 1bcd5808b442eb726763602525977bc064ecc0ee --- dom/events/AsyncEventDispatcher.cpp | 11 ++++++++++- dom/events/AsyncEventDispatcher.h | 17 ++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dom/events/AsyncEventDispatcher.cpp b/dom/events/AsyncEventDispatcher.cpp index 00786636cada..c256c9b47b21 100644 --- a/dom/events/AsyncEventDispatcher.cpp +++ b/dom/events/AsyncEventDispatcher.cpp @@ -23,7 +23,6 @@ using namespace dom; AsyncEventDispatcher::AsyncEventDispatcher(EventTarget* aTarget, WidgetEvent& aEvent) : mTarget(aTarget) - , mOnlyChromeDispatch(false) { MOZ_ASSERT(mTarget); RefPtr event = @@ -37,6 +36,9 @@ AsyncEventDispatcher::AsyncEventDispatcher(EventTarget* aTarget, NS_IMETHODIMP AsyncEventDispatcher::Run() { + if (mCanceled) { + return NS_OK; + } RefPtr event = mEvent ? mEvent->InternalDOMEvent() : nullptr; if (!event) { event = NS_NewDOMEvent(mTarget, nullptr, nullptr); @@ -52,6 +54,13 @@ AsyncEventDispatcher::Run() return NS_OK; } +NS_IMETHODIMP +AsyncEventDispatcher::Cancel() +{ + mCanceled = true; + return NS_OK; +} + nsresult AsyncEventDispatcher::PostDOMEvent() { diff --git a/dom/events/AsyncEventDispatcher.h b/dom/events/AsyncEventDispatcher.h index 5754876b896b..66438ceb5536 100644 --- a/dom/events/AsyncEventDispatcher.h +++ b/dom/events/AsyncEventDispatcher.h @@ -19,13 +19,13 @@ class nsINode; namespace mozilla { /** - * Use nsAsyncDOMEvent to fire a DOM event that requires safe a stable DOM. + * Use AsyncEventDispatcher to fire a DOM event that requires safe a stable DOM. * For example, you may need to fire an event from within layout, but * want to ensure that the event handler doesn't mutate the DOM at * the wrong time, in order to avoid resulting instability. */ - -class AsyncEventDispatcher : public nsRunnable + +class AsyncEventDispatcher : public nsCancelableRunnable { public: /** @@ -48,29 +48,28 @@ public: : mTarget(aTarget) , mEventType(aEventType) , mBubbles(aBubbles) - , mOnlyChromeDispatch(false) { } AsyncEventDispatcher(dom::EventTarget* aTarget, nsIDOMEvent* aEvent) : mTarget(aTarget) , mEvent(aEvent) - , mBubbles(false) - , mOnlyChromeDispatch(false) { } AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent); NS_IMETHOD Run() override; + NS_IMETHOD Cancel() override; nsresult PostDOMEvent(); void RunDOMEventWhenSafe(); nsCOMPtr mTarget; nsCOMPtr mEvent; nsString mEventType; - bool mBubbles; - bool mOnlyChromeDispatch; + bool mBubbles = false; + bool mOnlyChromeDispatch = false; + bool mCanceled = false; }; class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher @@ -96,7 +95,7 @@ public: mBlockedDoc->BlockOnload(); } } - + ~LoadBlockingAsyncEventDispatcher(); private: From b8972c7eb9299317d873b149b020e9d02ba74739 Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Sat, 12 Mar 2016 19:53:51 +0800 Subject: [PATCH 03/24] Bug 1225412 Part 2 - Add support to dispatch toggle events to details element. r=smaug Add ontoggle event handler, and dispatch toggle events to the details element if the open attribute is added or changed. According to the spec, if a new toggle event has been queued, previous toggle events should be aborted. MozReview-Commit-ID: EN6Jf5hVHHD --HG-- extra : rebase_source : 35605e49950bb59a0eb6dca594c3ede465ff587d --- dom/base/nsGkAtomList.h | 1 + dom/events/EventNameList.h | 4 ++ dom/html/HTMLDetailsElement.cpp | 19 ++++++ dom/html/HTMLDetailsElement.h | 25 +++++++- dom/webidl/EventHandler.webidl | 3 + .../meta/html/dom/interfaces.html.ini | 27 -------- .../the-details-element/toggleEvent.html.ini | 20 +----- .../the-details-element/toggleEvent.html | 64 +++++++++++++++++++ widget/EventMessageList.h | 3 + 9 files changed, 119 insertions(+), 47 deletions(-) diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 9ab703e7920a..2c8b39a59bb6 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -923,6 +923,7 @@ GK_ATOM(onsubmit, "onsubmit") GK_ATOM(onsuccess, "onsuccess") GK_ATOM(ontypechange, "ontypechange") GK_ATOM(ontext, "ontext") +GK_ATOM(ontoggle, "ontoggle") GK_ATOM(ontouchstart, "ontouchstart") GK_ATOM(ontouchend, "ontouchend") GK_ATOM(ontouchmove, "ontouchmove") diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index e1105276ee99..c86bcd6f100b 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -421,6 +421,10 @@ EVENT(timeupdate, eTimeUpdate, EventNameType_HTML, eBasicEventClass) +EVENT(toggle, + eToggle, + EventNameType_HTML, + eBasicEventClass) EVENT(volumechange, eVolumeChange, EventNameType_HTML, diff --git a/dom/html/HTMLDetailsElement.cpp b/dom/html/HTMLDetailsElement.cpp index 6085d7b8a2fd..5ab76f8b5a36 100644 --- a/dom/html/HTMLDetailsElement.cpp +++ b/dom/html/HTMLDetailsElement.cpp @@ -71,6 +71,25 @@ HTMLDetailsElement::GetAttributeChangeHint(const nsIAtom* aAttribute, return hint; } +nsresult +HTMLDetailsElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::open) { + bool setOpen = aValue != nullptr; + if (Open() != setOpen) { + if (mToggleEventDispatcher) { + mToggleEventDispatcher->Cancel(); + } + mToggleEventDispatcher = new ToggleEventDispatcher(this); + mToggleEventDispatcher->PostDOMEvent(); + } + } + + return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue, + aNotify); +} + JSObject* HTMLDetailsElement::WrapNode(JSContext* aCx, JS::Handle aGivenProto) { diff --git a/dom/html/HTMLDetailsElement.h b/dom/html/HTMLDetailsElement.h index caba9fe558f2..0bbfce373410 100644 --- a/dom/html/HTMLDetailsElement.h +++ b/dom/html/HTMLDetailsElement.h @@ -6,6 +6,7 @@ #ifndef mozilla_dom_HTMLDetailsElement_h #define mozilla_dom_HTMLDetailsElement_h +#include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Attributes.h" #include "nsGenericHTMLElement.h" @@ -38,12 +39,14 @@ public: nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const override; + nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, bool aNotify) override; + // HTMLDetailsElement WebIDL bool Open() const { return GetBoolAttr(nsGkAtoms::open); } void SetOpen(bool aOpen, ErrorResult& aError) { - // TODO: Bug 1225412: Need to follow the spec to fire "toggle" event. SetHTMLBoolAttr(nsGkAtoms::open, aOpen, aError); } @@ -59,6 +62,26 @@ protected: JSObject* WrapNode(JSContext* aCx, JS::Handle aGivenProto) override; + + class ToggleEventDispatcher final : public AsyncEventDispatcher + { + public: + // According to the html spec, a 'toggle' event is a simple event which does + // not bubble. + explicit ToggleEventDispatcher(nsINode* aTarget) + : AsyncEventDispatcher(aTarget, NS_LITERAL_STRING("toggle"), false) + { + } + + NS_IMETHOD Run() override + { + auto* details = static_cast(mTarget.get()); + details->mToggleEventDispatcher = nullptr; + return AsyncEventDispatcher::Run(); + } + }; + + RefPtr mToggleEventDispatcher; }; } // namespace dom diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl index 863f947bc6a8..eba8c4acba90 100644 --- a/dom/webidl/EventHandler.webidl +++ b/dom/webidl/EventHandler.webidl @@ -92,6 +92,9 @@ interface GlobalEventHandlers { [Pref="dom.select_events.enabled"] attribute EventHandler onselectstart; + [Pref="dom.details_element.enabled"] + attribute EventHandler ontoggle; + // Pointer events handlers [Pref="dom.w3c_pointer_events.enabled"] attribute EventHandler onpointercancel; diff --git a/testing/web-platform/meta/html/dom/interfaces.html.ini b/testing/web-platform/meta/html/dom/interfaces.html.ini index a541ef636d38..5b3bddc87891 100644 --- a/testing/web-platform/meta/html/dom/interfaces.html.ini +++ b/testing/web-platform/meta/html/dom/interfaces.html.ini @@ -136,9 +136,6 @@ [Document interface: attribute onsort] expected: FAIL - [Document interface: attribute ontoggle] - expected: FAIL - [Stringification of iframe.contentDocument] expected: FAIL @@ -205,9 +202,6 @@ [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (148)] expected: FAIL - [Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (153)] - expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "origin" with the proper type (3)] expected: FAIL @@ -409,9 +403,6 @@ [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (148)] expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (153)] - expected: FAIL - [Touch interface: attribute region] expected: FAIL @@ -592,9 +583,6 @@ [HTMLElement interface: attribute onsort] expected: FAIL - [HTMLElement interface: attribute ontoggle] - expected: FAIL - [HTMLElement interface: document.createElement("noscript") must inherit property "translate" with the proper type (2)] expected: FAIL @@ -646,9 +634,6 @@ [HTMLElement interface: document.createElement("noscript") must inherit property "onsort" with the proper type (87)] expected: FAIL - [HTMLElement interface: document.createElement("noscript") must inherit property "ontoggle" with the proper type (92)] - expected: FAIL - [Element interface: document.createElement("noscript") must inherit property "prepend" with the proper type (31)] expected: FAIL @@ -1965,9 +1950,6 @@ [Window interface: attribute onsort] expected: FAIL - [Window interface: attribute ontoggle] - expected: FAIL - [Window interface: operation createImageBitmap(ImageBitmapSource,long,long,long,long)] expected: FAIL @@ -2000,9 +1982,6 @@ [Window interface: window must inherit property "onsort" with the proper type (93)] expected: FAIL - [Window interface: window must inherit property "ontoggle" with the proper type (98)] - expected: FAIL - [Window interface: calling showModalDialog(DOMString,any) on window with too few arguments must throw TypeError] expected: if not e10s: PASS @@ -2515,9 +2494,6 @@ [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (149)] expected: FAIL - [Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (154)] - expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (32)] expected: FAIL @@ -2665,9 +2641,6 @@ [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (149)] expected: FAIL - [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (154)] - expected: FAIL - [Location interface: window.location must have own property "ancestorOrigins"] expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini b/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini index 42048f12797b..335ffd5b3a29 100644 --- a/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini +++ b/testing/web-platform/meta/html/semantics/interactive-elements/the-details-element/toggleEvent.html.ini @@ -1,21 +1,3 @@ [toggleEvent.html] type: testharness - expected: TIMEOUT - [Adding open to 'details' should fire a toggle event at the 'details' element] - expected: NOTRUN - - [Removing open from 'details' should fire a toggle event at the 'details' element] - expected: NOTRUN - - [Adding open to 'details' (display:none) should fire a toggle event at the 'details' element] - expected: NOTRUN - - [Adding open from 'details' (no children) should fire a toggle event at the 'details' element] - expected: NOTRUN - - [Calling open twice on 'details' fires only one toggle event] - expected: FAIL - - [Adding open to 'details' (not in the document) should fire a toggle event at the 'details' element] - expected: TIMEOUT - + prefs: [dom.details_element.enabled:true] diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html index 08d49c9bb9aa..56a239cfe2ae 100644 --- a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html +++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html @@ -24,17 +24,42 @@ Lorem ipsum

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ Lorem ipsum +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+ Lorem ipsum +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+ Lorem ipsum +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+ Lorem ipsum +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 5149ff457af8..b8fa41e7b37c 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -415,6 +415,9 @@ NS_EVENT_MESSAGE(eEditorInput) NS_EVENT_MESSAGE(eSelectStart) NS_EVENT_MESSAGE(eSelectionChange) +// Details element events. +NS_EVENT_MESSAGE(eToggle) + #ifdef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST #undef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST #undef NS_EVENT_MESSAGE_FIRST_LAST From d479c4cf4c67c58bdb91e0ceeda8d83c0740ec4e Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Mon, 14 Mar 2016 12:00:17 +0800 Subject: [PATCH 04/24] Bug 1225412 followup - Add comment to details10 in toggleEvent.html for consistency. r=me Comment-only so DONTBUILD. --HG-- extra : amend_source : 35f5ce9e53dd286224273e0aefbdcec7ca81e5e5 --- .../interactive-elements/the-details-element/toggleEvent.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html index 56a239cfe2ae..da255a3842d3 100644 --- a/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html +++ b/testing/web-platform/tests/html/semantics/interactive-elements/the-details-element/toggleEvent.html @@ -148,7 +148,7 @@ details10.ontoggle = t10.step_func_done(function(evt) { assert_unreached("toggle event fired on closed details element"); }); - details10.open = false; + details10.open = false; // closes details10 setTimeout(t10.step_func(function() { assert_false(details10.open); t10.done(); From 3edb5638a1626547d3e931fd067e798eb6e01654 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 11 Mar 2016 17:21:03 +0900 Subject: [PATCH 05/24] Bug 1246320 part 2 - Pass document to ParseEasing; r=hiro MozReview-Commit-ID: KWW53htO0Jj --HG-- extra : rebase_source : 85aaa0a66cae7623fa06ebf31e0b8481ee05bbde --- dom/animation/AnimationUtils.cpp | 19 +++++++------------ dom/animation/AnimationUtils.h | 6 +----- dom/animation/KeyframeEffect.cpp | 9 +++------ dom/animation/TimingParams.cpp | 2 +- 4 files changed, 12 insertions(+), 24 deletions(-) diff --git a/dom/animation/AnimationUtils.cpp b/dom/animation/AnimationUtils.cpp index a632496625ab..bab8bfa1f644 100644 --- a/dom/animation/AnimationUtils.cpp +++ b/dom/animation/AnimationUtils.cpp @@ -15,8 +15,7 @@ #include "nsString.h" #include "mozilla/Attributes.h" #include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction -#include "mozilla/dom/Element.h" // For dom::Element -#include "xpcpublic.h" // For xpc::CurrentWindowOrNull +#include "xpcpublic.h" // For xpc::NativeGlobal namespace mozilla { @@ -41,22 +40,18 @@ AnimationUtils::LogAsyncAnimationFailure(nsCString& aMessage, } /* static */ Maybe -AnimationUtils::ParseEasing(const dom::Element* aTarget, - const nsAString& aEasing) +AnimationUtils::ParseEasing(const nsAString& aEasing, + nsIDocument* aDocument) { - if (!aTarget) { - return Nothing(); - } - - nsIDocument* doc = aTarget->OwnerDoc(); + MOZ_ASSERT(aDocument); nsCSSValue value; nsCSSParser parser; parser.ParseLonghandProperty(eCSSProperty_animation_timing_function, aEasing, - doc->GetDocumentURI(), - doc->GetDocumentURI(), - doc->NodePrincipal(), + aDocument->GetDocumentURI(), + aDocument->GetDocumentURI(), + aDocument->NodePrincipal(), value); switch (value.GetUnit()) { diff --git a/dom/animation/AnimationUtils.h b/dom/animation/AnimationUtils.h index 0c26e0ada8e7..afe8e20a2a34 100644 --- a/dom/animation/AnimationUtils.h +++ b/dom/animation/AnimationUtils.h @@ -19,10 +19,6 @@ namespace mozilla { class ComputedTimingFunction; -namespace dom { -class Element; -} - class AnimationUtils { public: @@ -59,7 +55,7 @@ public: * be returned. */ static Maybe - ParseEasing(const dom::Element* aTarget, const nsAString& aEasing); + ParseEasing(const nsAString& aEasing, nsIDocument* aDocument); /** * Get the document from the JS context to use when parsing CSS properties. diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 5c0c9a2f510e..1062d67184f6 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -1287,10 +1287,9 @@ GenerateValueEntries(Element* aTarget, for (OffsetIndexedKeyframe& keyframe : aKeyframes) { float offset = float(keyframe.mKeyframeDict.mOffset.Value()); - // ParseEasing uses element's owner doc, so if it is a pseudo element, - // we use its parent element's owner doc. Maybe easing = - AnimationUtils::ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing); + AnimationUtils::ParseEasing(keyframe.mKeyframeDict.mEasing, + aTarget->OwnerDoc()); // We ignore keyframe.mKeyframeDict.mComposite since we don't support // composite modes on keyframes yet. @@ -1554,10 +1553,8 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes( return; } - // ParseEasing uses element's owner doc, so if it is a pseudo element, - // we use its parent element's owner doc. Maybe easing = - AnimationUtils::ParseEasing(aTarget, keyframes.mEasing); + AnimationUtils::ParseEasing(keyframes.mEasing, aTarget->OwnerDoc()); // We ignore easing.mComposite since we don't support composite modes on // keyframes yet. diff --git a/dom/animation/TimingParams.cpp b/dom/animation/TimingParams.cpp index 9c797d04ef19..0f5536b984d4 100644 --- a/dom/animation/TimingParams.cpp +++ b/dom/animation/TimingParams.cpp @@ -83,7 +83,7 @@ TimingParamsFromOptionsUnion( result.mDirection = timing.mDirection; result.mFill = timing.mFill; result.mFunction = - AnimationUtils::ParseEasing(targetElement, timing.mEasing); + AnimationUtils::ParseEasing(timing.mEasing, targetElement->OwnerDoc()); } return result; } From 036f647012269054b05e5a3fa00cf2150947fb63 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 11 Mar 2016 17:27:16 +0900 Subject: [PATCH 06/24] Bug 1246320 part 3 - Rework KeyframeEffect(ReadOnly) constructor helpers; r=hiro Once we update TimingParams to take a document, we will need to get an appropriate document within the various constructor methods. This complicates these methods and suggests they should be pushed into the .cpp file where we can hide the complexity more easily and templatize the type of the options argument so that we can share the document-fetching code. By moving all uses of the declared template methods to the .cpp file we can drop the explicit instantiations. (We still need to declare the templated methods in the header file since these methods need to be protected methods of KeyframeEffectReadOnly in order to construct a KeyframeEffectReadOnly since its constructor is protected.) MozReview-Commit-ID: 8KrCWrWIb7X --HG-- extra : rebase_source : c5b550b271cc68ceeb60a25243268a17b3ab7f65 --- dom/animation/KeyframeEffect.cpp | 85 +++++++++++++++++++++++--------- dom/animation/KeyframeEffect.h | 47 ++++++------------ 2 files changed, 78 insertions(+), 54 deletions(-) diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 1062d67184f6..25e1c42dcc49 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -652,13 +652,33 @@ KeyframeEffectReadOnly::~KeyframeEffectReadOnly() { } +template +/* static */ already_AddRefed +KeyframeEffectReadOnly::ConstructKeyframeEffect( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const OptionsType& aOptions, + ErrorResult& aRv) +{ + TimingParams timingParams = + TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); + if (aRv.Failed()) { + return nullptr; + } + + return ConstructKeyframeEffect( + aGlobal, aTarget, aFrames, timingParams, aRv); +} + template /* static */ already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv) +KeyframeEffectReadOnly::ConstructKeyframeEffect( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const TimingParams& aTiming, + ErrorResult& aRv) { if (aTarget.IsNull()) { // We don't support null targets yet. @@ -699,24 +719,6 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal, return effect.forget(); } -// Explicit instantiations to avoid linker errors. - -template -already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv); - -template -already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv); - void KeyframeEffectReadOnly::ResetIsRunningOnCompositor() { @@ -1754,6 +1756,19 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList( } } +/* static */ already_AddRefed +KeyframeEffectReadOnly::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, + aFrames, aOptions, + aRv); +} + void KeyframeEffectReadOnly::GetTarget( Nullable& aRv) const @@ -2212,6 +2227,30 @@ KeyframeEffect::WrapObject(JSContext* aCx, return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto); } +/* static */ already_AddRefed +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, + aOptions, aRv); +} + +/* static */ already_AddRefed +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const TimingParams& aTiming, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, + aTiming, aRv); +} + void KeyframeEffect::NotifySpecifiedTimingUpdated() { nsIDocument* doc = nullptr; diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 7f44bf1c1850..a5b963f2dfc8 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -202,16 +202,7 @@ public: const Nullable& aTarget, JS::Handle aFrames, const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - ErrorResult& aRv) - { - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); - if (aRv.Failed()) { - return nullptr; - } - return ConstructKeyframeEffect( - aGlobal, aTarget, aFrames, timingParams, aRv); - } + ErrorResult& aRv); void GetTarget(Nullable& aRv) const; void GetFrames(JSContext*& aCx, @@ -354,7 +345,14 @@ protected: virtual ~KeyframeEffectReadOnly(); - template + template + static already_AddRefed + ConstructKeyframeEffect(const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const OptionsType& aOptions, + ErrorResult& aRv); + template static already_AddRefed ConstructKeyframeEffect(const GlobalObject& aGlobal, const Nullable& aTarget, @@ -433,29 +431,16 @@ public: const Nullable& aTarget, JS::Handle aFrames, const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - ErrorResult& aRv) - { - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); - if (aRv.Failed()) { - return nullptr; - } - return ConstructKeyframeEffect( - aGlobal, aTarget, aFrames, timingParams, aRv); - } + ErrorResult& aRv); - // More generalized version for Animatable.animate. + // More generalized version of Constructor for Animatable.animate. // Not exposed to content. static already_AddRefed - inline Constructor(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv) - { - return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, - aTiming, aRv); - } + Constructor(const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const TimingParams& aTiming, + ErrorResult& aRv); void NotifySpecifiedTimingUpdated(); From 6846d9e23bac62e07b990cd5ade8724e0ba2b755 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 11 Mar 2016 17:27:34 +0900 Subject: [PATCH 07/24] Bug 1246320 part 4 - Pass a document to TimingParams; r=hiro MozReview-Commit-ID: 9Sh8eWHdDD6 --HG-- extra : rebase_source : 2dc0da7076b8acaaca257583f3780266ae62b430 --- dom/animation/KeyframeEffect.cpp | 8 +++++++- dom/animation/TimingParams.cpp | 35 +++++++++----------------------- dom/animation/TimingParams.h | 15 ++++++-------- dom/base/Element.cpp | 3 ++- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 25e1c42dcc49..36db3ea5c84e 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -661,8 +661,14 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect( const OptionsType& aOptions, ErrorResult& aRv) { + nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context()); + if (!doc) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); + TimingParams::FromOptionsUnion(aOptions, doc, aRv); if (aRv.Failed()) { return nullptr; } diff --git a/dom/animation/TimingParams.cpp b/dom/animation/TimingParams.cpp index 0f5536b984d4..0092bfeefc54 100644 --- a/dom/animation/TimingParams.cpp +++ b/dom/animation/TimingParams.cpp @@ -6,6 +6,8 @@ #include "mozilla/TimingParams.h" +#include "nsIDocument.h" + namespace mozilla { template @@ -32,10 +34,9 @@ GetTimingProperties( template static TimingParams -TimingParamsFromOptionsUnion( - const OptionsType& aOptions, - const Nullable& aTarget, - ErrorResult& aRv) +TimingParamsFromOptionsUnion(const OptionsType& aOptions, + nsIDocument* aDocument, + ErrorResult& aRv) { TimingParams result; if (aOptions.IsUnrestrictedDouble()) { @@ -47,21 +48,6 @@ TimingParamsFromOptionsUnion( aRv.Throw(NS_ERROR_DOM_TYPE_ERR); } } else { - // If aTarget is a pseudo element, we pass its parent element because - // TimingParams only needs its owner doc to parse easing and both pseudo - // element and its parent element should have the same owner doc. - // Bug 1246320: Avoid passing the element for parsing the timing function - RefPtr targetElement; - if (!aTarget.IsNull()) { - const dom::ElementOrCSSPseudoElement& target = aTarget.Value(); - MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(), - "Uninitialized target"); - if (target.IsElement()) { - targetElement = &target.GetAsElement(); - } else { - targetElement = target.GetAsCSSPseudoElement().ParentElement(); - } - } const dom::AnimationEffectTimingProperties& timing = GetTimingProperties(aOptions); @@ -82,8 +68,7 @@ TimingParamsFromOptionsUnion( result.mIterationStart = timing.mIterationStart; result.mDirection = timing.mDirection; result.mFill = timing.mFill; - result.mFunction = - AnimationUtils::ParseEasing(timing.mEasing, targetElement->OwnerDoc()); + result.mFunction = AnimationUtils::ParseEasing(timing.mEasing, aDocument); } return result; } @@ -91,19 +76,19 @@ TimingParamsFromOptionsUnion( /* static */ TimingParams TimingParams::FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - const Nullable& aTarget, + nsIDocument* aDocument, ErrorResult& aRv) { - return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv); + return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv); } /* static */ TimingParams TimingParams::FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, - const Nullable& aTarget, + nsIDocument* aDocument, ErrorResult& aRv) { - return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv); + return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv); } bool diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h index 22cda11fe081..7c3a4320e6ca 100644 --- a/dom/animation/TimingParams.h +++ b/dom/animation/TimingParams.h @@ -17,17 +17,16 @@ #ifdef None #undef None #endif -#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode - // and PlaybackDirection +#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode + // and PlaybackDirection + +class nsIDocument; namespace mozilla { namespace dom { -struct AnimationEffectTimingProperties; -class Element; class UnrestrictedDoubleOrKeyframeEffectOptions; class UnrestrictedDoubleOrKeyframeAnimationOptions; -class ElementOrCSSPseudoElement; } struct TimingParams @@ -36,12 +35,10 @@ struct TimingParams static TimingParams FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - const Nullable& aTarget, - ErrorResult& aRv); + nsIDocument* aDocument, ErrorResult& aRv); static TimingParams FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, - const Nullable& aTarget, - ErrorResult& aRv); + nsIDocument* aDocument, ErrorResult& aRv); // Range-checks and validates an UnrestrictedDoubleOrString or // OwningUnrestrictedDoubleOrString object and converts to a diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 2bb7fb584d99..9d96545a35a5 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3354,7 +3354,8 @@ Element::Animate(const Nullable& aTarget, } TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aError); + TimingParams::FromOptionsUnion(aOptions, referenceElement->OwnerDoc(), + aError); if (aError.Failed()) { return nullptr; } From 107642839fa035596957a37e424222a476babbab Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Sat, 12 Mar 2016 22:14:10 +0900 Subject: [PATCH 08/24] Bug 1246320 part 5 - Simplify KeyframeEffect(ReadOnly) Constructor overloads further; r=hiro As well as generally simplifying the different KeyframeEffect(ReadOnly) constructor methods, this patch also means we will use the realm document for parsing timing functions in all cases. Although this currently doesn't have any impact (the current set of timing functions are expected to be parsed identically regardless of the document used) it may become significant if, in future, it becomes possible to register hooks with certain documents for parsing CSS properties as part of the houdini efforts. MozReview-Commit-ID: 4gAZi1G1uAD --HG-- extra : rebase_source : f619592a02ddcbe56835344ec1fb3023219cd2d3 --- dom/animation/KeyframeEffect.cpp | 20 ++++---------------- dom/animation/KeyframeEffect.h | 13 ++++--------- dom/base/Element.cpp | 9 +-------- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 36db3ea5c84e..60b88c50c5a7 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/dom/AnimatableBinding.h" #include "mozilla/dom/KeyframeEffectBinding.h" #include "mozilla/dom/PropertyIndexedKeyframesBinding.h" #include "mozilla/AnimationUtils.h" @@ -673,19 +674,6 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect( return nullptr; } - return ConstructKeyframeEffect( - aGlobal, aTarget, aFrames, timingParams, aRv); -} - -template -/* static */ already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect( - const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv) -{ if (aTarget.IsNull()) { // We don't support null targets yet. aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR); @@ -720,7 +708,7 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect( RefPtr effect = new KeyframeEffectType(targetElement->OwnerDoc(), targetElement, - pseudoType, aTiming); + pseudoType, timingParams); effect->mProperties = Move(animationProperties); return effect.forget(); } @@ -2250,11 +2238,11 @@ KeyframeEffect::Constructor( const GlobalObject& aGlobal, const Nullable& aTarget, JS::Handle aFrames, - const TimingParams& aTiming, + const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, ErrorResult& aRv) { return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, - aTiming, aRv); + aOptions, aRv); } void KeyframeEffect::NotifySpecifiedTimingUpdated() diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index a5b963f2dfc8..6b64e85a6a50 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -42,6 +42,7 @@ enum class CSSPseudoElementType : uint8_t; namespace dom { class ElementOrCSSPseudoElement; class OwningElementOrCSSPseudoElement; +class UnrestrictedDoubleOrKeyframeAnimationOptions; class UnrestrictedDoubleOrKeyframeEffectOptions; enum class IterationCompositeOperation : uint32_t; enum class CompositeOperation : uint32_t; @@ -352,13 +353,6 @@ protected: JS::Handle aFrames, const OptionsType& aOptions, ErrorResult& aRv); - template - static already_AddRefed - ConstructKeyframeEffect(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv); void ResetIsRunningOnCompositor(); @@ -433,13 +427,14 @@ public: const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, ErrorResult& aRv); - // More generalized version of Constructor for Animatable.animate. + // Variant of Constructor that accepts a KeyframeAnimationOptions object + // for use with for Animatable.animate. // Not exposed to content. static already_AddRefed Constructor(const GlobalObject& aGlobal, const Nullable& aTarget, JS::Handle aFrames, - const TimingParams& aTiming, + const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, ErrorResult& aRv); void NotifySpecifiedTimingUpdated(); diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 9d96545a35a5..d60cef5a9311 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3353,15 +3353,8 @@ Element::Animate(const Nullable& aTarget, } } - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, referenceElement->OwnerDoc(), - aError); - if (aError.Failed()) { - return nullptr; - } - RefPtr effect = - KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError); + KeyframeEffect::Constructor(global, aTarget, frames, aOptions, aError); if (aError.Failed()) { return nullptr; } From 45a7f9a6791de251aa9582e0ad3d63ddcf5b11eb Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Mon, 14 Mar 2016 09:07:48 +0900 Subject: [PATCH 09/24] Bug 1218620 - Allow opacity animation running on compositor even if the frame has any restricted transforms. r=birtles The type name has been changed and re-ordered. MozReview-Commit-ID: 78jrJ6a9Pro --HG-- extra : rebase_source : f47e6bf27d8e48d10b3af123308c2ab89e71d8e1 --- dom/animation/AnimationPerformanceWarning.cpp | 6 +- dom/animation/AnimationPerformanceWarning.h | 2 +- dom/animation/EffectCompositor.cpp | 10 +- dom/animation/KeyframeEffect.cpp | 4 +- dom/animation/KeyframeEffect.h | 22 +- .../chrome/test_animation_property_state.html | 386 +++++++++++++++++- .../chrome/layout/layout_errors.properties | 6 +- 7 files changed, 405 insertions(+), 31 deletions(-) diff --git a/dom/animation/AnimationPerformanceWarning.cpp b/dom/animation/AnimationPerformanceWarning.cpp index 21b91e3525b1..207bf05c54ff 100644 --- a/dom/animation/AnimationPerformanceWarning.cpp +++ b/dom/animation/AnimationPerformanceWarning.cpp @@ -52,15 +52,15 @@ AnimationPerformanceWarning::ToLocalizedString( case Type::TransformSVG: key = "AnimationWarningTransformSVG"; break; + case Type::TransformWithGeometricProperties: + key = "AnimationWarningTransformWithGeometricProperties"; + break; case Type::TransformFrameInactive: key = "AnimationWarningTransformFrameInactive"; break; case Type::OpacityFrameInactive: key = "AnimationWarningOpacityFrameInactive"; break; - case Type::WithGeometricProperties: - key = "AnimationWarningWithGeometricProperties"; - break; } nsresult rv = diff --git a/dom/animation/AnimationPerformanceWarning.h b/dom/animation/AnimationPerformanceWarning.h index 11481fe437da..4eb25bad2e5e 100644 --- a/dom/animation/AnimationPerformanceWarning.h +++ b/dom/animation/AnimationPerformanceWarning.h @@ -21,9 +21,9 @@ struct AnimationPerformanceWarning TransformBackfaceVisibilityHidden, TransformPreserve3D, TransformSVG, + TransformWithGeometricProperties, TransformFrameInactive, OpacityFrameInactive, - WithGeometricProperties }; explicit AnimationPerformanceWarning(Type aType) diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp index 4c2c80e7ce07..eedc421dd33f 100644 --- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -111,13 +111,15 @@ FindAnimationsForCompositor(const nsIFrame* aFrame, } AnimationPerformanceWarning::Type warningType; - if (effect->ShouldBlockCompositorAnimations(aFrame, - warningType)) { + if (aProperty == eCSSProperty_transform && + effect->ShouldBlockAsyncTransformAnimations(aFrame, + warningType)) { if (aMatches) { aMatches->Clear(); } - effect->SetPerformanceWarning( - aProperty, AnimationPerformanceWarning(warningType)); + EffectCompositor::SetPerformanceWarning( + aFrame, aProperty, + AnimationPerformanceWarning(warningType)); return false; } diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 60b88c50c5a7..fb130e9340b0 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -2141,7 +2141,7 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor( } bool -KeyframeEffectReadOnly::ShouldBlockCompositorAnimations( +KeyframeEffectReadOnly::ShouldBlockAsyncTransformAnimations( const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aPerformanceWarning) const { @@ -2161,7 +2161,7 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations( // Check for geometric properties if (IsGeometricProperty(property.mProperty)) { aPerformanceWarning = - AnimationPerformanceWarning::Type::WithGeometricProperties; + AnimationPerformanceWarning::Type::TransformWithGeometricProperties; return true; } diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 6b64e85a6a50..b1f6b7fdc3d0 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -306,24 +306,16 @@ public: void GetPropertyState(nsTArray& aStates) const; - // Returns true if this effect, applied to |aFrame|, contains - // properties that mean we shouldn't run *any* compositor animations on this - // element. + // Returns true if this effect, applied to |aFrame|, contains properties + // that mean we shouldn't run transform compositor animations on this element. // // For example, if we have an animation of geometric properties like 'left' - // and 'top' on an element, we force all 'transform' and 'opacity' animations - // running at the same time on the same element to run on the main thread. + // and 'top' on an element, we force all 'transform' animations running at + // the same time on the same element to run on the main thread. // - // Similarly, some transform animations cannot be run on the compositor and - // when that is the case we simply disable all compositor animations - // on the same element. - // - // Bug 1218620 - It seems like we don't need to be this restrictive. Wouldn't - // it be ok to do 'opacity' animations on the compositor in either case? - // - // When returning true, |aOutPerformanceWarning| stores the reason why - // we shouldn't run the compositor animations. - bool ShouldBlockCompositorAnimations( + // When returning true, |aPerformanceWarning| stores the reason why + // we shouldn't run the transform animations. + bool ShouldBlockAsyncTransformAnimations( const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aPerformanceWarning) const; diff --git a/dom/animation/test/chrome/test_animation_property_state.html b/dom/animation/test/chrome/test_animation_property_state.html index 0dcce0200d1f..0e4041460711 100644 --- a/dom/animation/test/chrome/test_animation_property_state.html +++ b/dom/animation/test/chrome/test_animation_property_state.html @@ -79,7 +79,8 @@ function assert_property_state_on_compositor(actual, expected) { sortedExpected[i].property, 'CSS property name should match'); assert_true(sortedActual[i].runningOnCompositor, - 'runningOnCompositor property should be true'); + 'runningOnCompositor property should be true on ' + + sortedActual[i].property); assert_not_exists(sortedActual[i], 'warning', 'warning property should not be set'); } @@ -144,11 +145,28 @@ var gAnimationsTests = [ } ] }, + { + desc: 'opacity on compositor with animation of geometric properties', + frames: { + width: ['100px', '200px'], + opacity: [0, 1] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false + }, + { + property: 'opacity', + runningOnCompositor: true + } + ] + }, { // FIXME: Once we have KeyframeEffect.setFrames, we should rewrite // this test case to check that runningOnCompositor is restored to true // after 'width' keyframe is removed from the keyframes. - desc: 'animation on compositor with animation of geometric properties', + desc: 'transform on compositor with animation of geometric properties', frames: { width: ['100px', '200px'], transform: ['translate(0px)', 'translate(100px)'] @@ -161,7 +179,31 @@ var gAnimationsTests = [ { property: 'transform', runningOnCompositor: false, - warning: 'AnimationWarningWithGeometricProperties' + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + desc: 'opacity and transform on compositor with animation of geometric ' + + 'properties', + frames: { + width: ['100px', '200px'], + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false + }, + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' } ] }, @@ -208,6 +250,243 @@ var gPerformanceWarningTests = [ } ] }, + { + desc: 'opacity and transform with preserve-3d', + frames: { + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + style: 'transform-style: preserve-3d', + expected: [ + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformPreserve3D' + } + ] + }, + { + desc: 'opacity and transform with backface-visibility:hidden', + frames: { + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + style: 'backface-visibility: hidden;', + expected: [ + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformBackfaceVisibilityHidden' + } + ] + }, +]; + +var gMultipleAsyncAnimationsTests = [ + { + desc: 'opacity and transform with preserve-3d', + style: 'transform-style: preserve-3d', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformPreserve3D' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, + { + desc: 'opacity and transform with backface-visibility:hidden', + style: 'backface-visibility: hidden;', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformBackfaceVisibilityHidden' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, +]; + +// FIXME: Once we have KeyframeEffect.setFrames, we should rewrite +// these test cases to check that runningOnCompositor is restored to true +// after 'width' keyframe is removed from the keyframes. +var gMultipleAsyncAnimationsWithGeometricKeyframeTests = [ + { + desc: 'transform and opacity with animation of geometric properties', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + width: ['100px', '200px'], + opacity: [0, 1] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false, + }, + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, + { + desc: 'opacity and transform with animation of geometric properties', + animations: [ + { + frames: { + width: ['100px', '200px'], + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false, + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, +]; + +// Test cases that check results of adding/removing 'width' animation on the +// same element which has async animations. +var gMultipleAsyncAnimationsWithGeometricAnimationTests = [ + { + desc: 'transform', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + ] + }, + { + desc: 'opacity', + animations: [ + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true + } + ] + }, + ] + }, + { + desc: 'opacity and transform', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, ]; function start() { @@ -252,6 +531,107 @@ function start() { }, subtest.desc); }); + gMultipleAsyncAnimationsTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getPropertyState(), + anim.expected); + })); + div.style = subtest.style; + return waitForFrame(); + })).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getPropertyState(), + anim.expected); + })); + div.style = ''; + return waitForFrame(); + })).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getPropertyState(), + anim.expected); + })); + })); + }, 'Multiple animations: ' + subtest.desc); + }); + + gMultipleAsyncAnimationsWithGeometricKeyframeTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getPropertyState(), + anim.expected); + })); + })); + }, 'Multiple animations with geometric property: ' + subtest.desc); + }); + + gMultipleAsyncAnimationsWithGeometricAnimationTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + + var widthAnimation; + + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getPropertyState(), + anim.expected); + })); + })).then(t.step_func(function() { + // Append 'width' animation on the same element. + widthAnimation = div.animate({ width: ['100px', '200px'] }, 100000); + return waitForFrame(); + })).then(t.step_func(function() { + // Now transform animations are not running on compositor because of + // the 'width' animation. + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getPropertyState(), + anim.expected); + })); + // Remove the 'width' animation. + widthAnimation.cancel(); + return waitForFrame(); + })).then(t.step_func(function() { + // Now all animations are running on compositor. + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getPropertyState(), + anim.expected); + })); + })); + }, 'Multiple async animations and geometric animation: ' + subtest.desc); + }); + promise_test(function(t) { var div = addDiv(t, { class: 'compositable' }); var animation = div.animate( diff --git a/dom/locales/en-US/chrome/layout/layout_errors.properties b/dom/locales/en-US/chrome/layout/layout_errors.properties index 7ae736b25db5..e43bcd7c84b6 100644 --- a/dom/locales/en-US/chrome/layout/layout_errors.properties +++ b/dom/locales/en-US/chrome/layout/layout_errors.properties @@ -24,11 +24,11 @@ AnimationWarningTransformBackfaceVisibilityHidden=Async animation of 'backface-v ## 'transform-style: preserve-3d' is a CSS property, don't translate it. AnimationWarningTransformPreserve3D=Async animation of 'transform-style: preserve-3d' transforms is not supported ## LOCALIZATION NOTE(AnimationWarningTransformSVG, +## AnimationWarningTransformWithGeometricProperties, ## AnimationWarningTransformFrameInactive, -## AnimationWarningOpacityFrameInactive, -## AnimationWarningWithGeometricProperties): +## AnimationWarningOpacityFrameInactive): ## 'transform' and 'opacity' mean CSS property names, don't translate it. AnimationWarningTransformSVG=Async 'transform' animations of aFrames with SVG transforms is not supported +AnimationWarningTransformWithGeometricProperties=Async animation of 'transform' not possible due to animation of geometric properties on the same element AnimationWarningTransformFrameInactive=Async animation disabled because frame was not marked active for 'transform' animation AnimationWarningOpacityFrameInactive=Async animation disabled because frame was not marked active for 'opacity' animation -AnimationWarningWithGeometricProperties=Async animation of 'transform' or 'opacity' not possible due to animation of geometric properties on the same element From 4e06af5a131f08162165912817898b2ce4ac98e2 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Mon, 14 Mar 2016 13:08:11 +0900 Subject: [PATCH 10/24] Bug 1218620 - Part 2: Skip all 'preserve-3d' tests which breaks other compositor frames. r=birtles MozReview-Commit-ID: 85dk5yOYizZ --HG-- extra : rebase_source : a2cc3fa74b4bc967b58129458ab084e2a56acc74 --- .../test/chrome/test_animation_property_state.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dom/animation/test/chrome/test_animation_property_state.html b/dom/animation/test/chrome/test_animation_property_state.html index 0e4041460711..3c4c8668758b 100644 --- a/dom/animation/test/chrome/test_animation_property_state.html +++ b/dom/animation/test/chrome/test_animation_property_state.html @@ -508,6 +508,11 @@ function start() { }); gPerformanceWarningTests.forEach(function(subtest) { + // FIXME: Bug 1255710: 'preserve-3d' frame breaks other tests, + // we should skip all 'preserve-3d' tests here. + if (subtest.desc.includes('preserve-3d')) { + return; + } promise_test(function(t) { var div = addDiv(t, { class: 'compositable' }); var animation = div.animate(subtest.frames, 100000); @@ -532,6 +537,11 @@ function start() { }); gMultipleAsyncAnimationsTests.forEach(function(subtest) { + // FIXME: Bug 1255710: 'preserve-3d' frame breaks other tests, + // we should skip all 'preserve-3d' tests here. + if (subtest.desc.includes('preserve-3d')) { + return; + } promise_test(function(t) { var div = addDiv(t, { class: 'compositable' }); var animations = subtest.animations.map(function(anim) { From 73c9a25e3648d7d40de2fca74586beb6f926c017 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Fri, 4 Dec 2015 11:58:55 +0800 Subject: [PATCH 11/24] Bug 1223729 - HID Connection Implementation, r=jocelyn, sr=mrbkap --HG-- rename : dom/bluetooth/common/BluetoothHidManager.cpp => dom/bluetooth/bluez/BluetoothHidManager.cpp rename : dom/bluetooth/common/BluetoothHidManager.h => dom/bluetooth/bluez/BluetoothHidManager.h --- dom/base/nsGkAtomList.h | 1 + .../bluedroid/BluetoothDaemonHelpers.cpp | 249 +++++++ .../bluedroid/BluetoothDaemonHelpers.h | 42 ++ .../bluedroid/BluetoothDaemonHidInterface.cpp | 677 ++++++++++++++++++ .../bluedroid/BluetoothDaemonHidInterface.h | 298 ++++++++ .../bluedroid/BluetoothDaemonInterface.cpp | 28 +- .../bluedroid/BluetoothDaemonInterface.h | 3 + .../bluedroid/BluetoothHidManager.cpp | 610 ++++++++++++++++ dom/bluetooth/bluedroid/BluetoothHidManager.h | 71 ++ .../bluedroid/BluetoothServiceBluedroid.cpp | 4 + .../{common => bluez}/BluetoothHidManager.cpp | 0 .../{common => bluez}/BluetoothHidManager.h | 0 dom/bluetooth/common/BluetoothCommon.h | 66 ++ dom/bluetooth/common/BluetoothInterface.cpp | 105 +++ dom/bluetooth/common/BluetoothInterface.h | 125 ++++ .../common/BluetoothProfileController.cpp | 1 + .../common/webapi/BluetoothAdapter.cpp | 1 + .../common/webapi/BluetoothAdapter.h | 3 +- dom/bluetooth/moz.build | 4 +- dom/webidl/BluetoothAdapter.webidl | 3 + 20 files changed, 2288 insertions(+), 3 deletions(-) create mode 100644 dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp create mode 100644 dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h create mode 100644 dom/bluetooth/bluedroid/BluetoothHidManager.cpp create mode 100644 dom/bluetooth/bluedroid/BluetoothHidManager.h rename dom/bluetooth/{common => bluez}/BluetoothHidManager.cpp (100%) rename dom/bluetooth/{common => bluez}/BluetoothHidManager.h (100%) diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 2c8b39a59bb6..1bf5e77d8fc9 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -798,6 +798,7 @@ GK_ATOM(onhashchange, "onhashchange") GK_ATOM(onheadphoneschange, "onheadphoneschange") GK_ATOM(onheld, "onheld") GK_ATOM(onhfpstatuschanged, "onhfpstatuschanged") +GK_ATOM(onhidstatuschanged, "onhidstatuschanged") GK_ATOM(onholding, "onholding") GK_ATOM(oniccchange, "oniccchange") GK_ATOM(oniccdetected, "oniccdetected") diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp index f35aadba218f..d6b037a2162d 100644 --- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp +++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.cpp @@ -514,6 +514,78 @@ Convert(int32_t aIn, BluetoothGattStatus& aOut) return NS_OK; } +nsresult +Convert(uint8_t aIn, BluetoothHidProtocolMode& aOut) +{ + static const BluetoothHidProtocolMode sMode[] = { + [0x00] = HID_PROTOCOL_MODE_REPORT, + [0x01] = HID_PROTOCOL_MODE_BOOT + }; + if (aIn == 0xff) { + /* This case is handled separately to not populate + * |sMode| with empty entries. */ + aOut = HID_PROTOCOL_MODE_UNSUPPORTED; + return NS_OK; + } + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sMode), uint8_t, BluetoothHidProtocolMode)) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sMode[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothHidConnectionState& aOut) +{ + static const BluetoothHidConnectionState sConnectionState[] = { + [0x00] = HID_CONNECTION_STATE_CONNECTED, + [0x01] = HID_CONNECTION_STATE_CONNECTING, + [0x02] = HID_CONNECTION_STATE_DISCONNECTED, + [0x03] = HID_CONNECTION_STATE_DISCONNECTING, + [0x04] = HID_CONNECTION_STATE_FAILED_MOUSE_FROM_HOST, + [0x05] = HID_CONNECTION_STATE_FAILED_KEYBOARD_FROM_HOST, + [0x06] = HID_CONNECTION_STATE_FAILED_TOO_MANY_DEVICES, + [0x07] = HID_CONNECTION_STATE_FAILED_NO_HID_DRIVER, + [0x08] = HID_CONNECTION_STATE_FAILED_GENERIC, + [0x09] = HID_CONNECTION_STATE_UNKNOWN + }; + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sConnectionState), + uint8_t, BluetoothHidConnectionState)) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sConnectionState[aIn]; + return NS_OK; +} + +nsresult +Convert(uint8_t aIn, BluetoothHidStatus& aOut) +{ + static const BluetoothHidStatus sStatus[] = { + [0x00] = HID_STATUS_OK, + [0x01] = HID_STATUS_HANDSHAKE_DEVICE_NOT_READY, + [0x02] = HID_STATUS_HANDSHAKE_INVALID_REPORT_ID, + [0x03] = HID_STATUS_HANDSHAKE_TRANSACTION_NOT_SPT, + [0x04] = HID_STATUS_HANDSHAKE_INVALID_PARAMETER, + [0x05] = HID_STATUS_HANDSHAKE_GENERIC_ERROR, + [0x06] = HID_STATUS_GENERAL_ERROR, + [0x07] = HID_STATUS_SDP_ERROR, + [0x08] = HID_STATUS_SET_PROTOCOL_ERROR, + [0x09] = HID_STATUS_DEVICE_DATABASE_FULL, + [0x0a] = HID_STATUS_DEVICE_TYPE_NOT_SUPPORTED, + [0x0b] = HID_STATUS_NO_RESOURCES, + [0x0c] = HID_STATUS_AUTHENTICATION_FAILED, + [0x0d] = HID_STATUS_HDL + }; + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sStatus), uint8_t, BluetoothHidStatus)) { + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sStatus[aIn]; + return NS_OK; +} + nsresult Convert(nsresult aIn, BluetoothStatus& aOut) { @@ -981,6 +1053,47 @@ Convert(BluetoothGattWriteType aIn, int32_t& aOut) return NS_OK; } +nsresult +Convert(BluetoothHidProtocolMode aIn, uint8_t& aOut) +{ + static const uint8_t sMode[] = { + [HID_PROTOCOL_MODE_REPORT] = 0x00, + [HID_PROTOCOL_MODE_BOOT] = 0x01 + }; + if (aIn == HID_PROTOCOL_MODE_UNSUPPORTED) { + /* This case is handled separately to not populate + * |sValue| with empty entries. */ + aOut = 0xff; + return NS_OK; + } + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sMode), BluetoothHidProtocolMode, uint8_t)) { + aOut = 0x00; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sMode[aIn]; + return NS_OK; +} + +nsresult +Convert(BluetoothHidReportType aIn, uint8_t& aOut) +{ + static const uint8_t sType[] = { + [0x00] = static_cast(0), + [HID_REPORT_TYPE_INPUT] = 0x01, + [HID_REPORT_TYPE_OUTPUT] = 0x02, + [HID_REPORT_TYPE_FEATURE] = 0x03 + }; + if (MOZ_HAL_IPC_CONVERT_WARN_IF( + aIn >= MOZ_ARRAY_LENGTH(sType), + BluetoothHidReportType, uint8_t)) { + aOut = 0x00; // silences compiler warning + return NS_ERROR_ILLEGAL_VALUE; + } + aOut = sType[aIn]; + return NS_OK; +} + /* |ConvertArray| is a helper for converting arrays. Pass an * instance of this structure as the first argument to |Convert| * to convert an array. The output type has to support the array @@ -1406,6 +1519,61 @@ PackPDU(BluetoothGattWriteType aIn, DaemonSocketPDU& aPDU) return PackPDU(PackConversion(aIn), aPDU); } +nsresult +PackPDU(const BluetoothHidInfoParam& aIn, DaemonSocketPDU& aPDU) +{ + if (MOZ_HAL_IPC_PACK_WARN_IF( + aIn.mDescriptorLength > BLUETOOTH_HID_MAX_DESC_LEN, + BluetoothHidInfoParam)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + nsresult rv = PackPDU(aIn.mAttributeMask, + aIn.mSubclass, + aIn.mApplicationId, + aIn.mVendorId, + aIn.mProductId, + aIn.mVersion, + aIn.mCountryCode, + aIn.mDescriptorLength, + aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU( + PackArray(aIn.mDescriptorValue, aIn.mDescriptorLength), aPDU); +} + +nsresult +PackPDU(const BluetoothHidReport& aIn, DaemonSocketPDU& aPDU) +{ + uint8_t* reportData = + const_cast(aIn.mReportData.Elements()); + + nsresult rv = PackPDU( + PackConversion(aIn.mReportData.Length()), aPDU); + if (NS_FAILED(rv)) { + return rv; + } + return PackPDU( + PackArray(reportData, aIn.mReportData.Length()), + aPDU); +} + +nsresult +PackPDU(BluetoothHidProtocolMode aIn, DaemonSocketPDU& aPDU) +{ + return PackPDU( + PackConversion(aIn), aPDU); +} + +nsresult +PackPDU(BluetoothHidReportType aIn, DaemonSocketPDU& aPDU) +{ + return PackPDU( + PackConversion(aIn), aPDU); +} + // // Unpacking // @@ -1813,4 +1981,85 @@ UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattNotifyParam& aOut) return aPDU.Read(aOut.mValue, aOut.mLength); } +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidInfoParam& aOut) +{ + /* unpack attribute mask */ + nsresult rv = UnpackPDU(aPDU, aOut.mAttributeMask); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack subclass */ + rv = UnpackPDU(aPDU, aOut.mSubclass); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack application id */ + rv = UnpackPDU(aPDU, aOut.mApplicationId); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack vendor id */ + rv = UnpackPDU(aPDU, aOut.mVendorId); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack product id */ + rv = UnpackPDU(aPDU, aOut.mProductId); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack version */ + rv = UnpackPDU(aPDU, aOut.mVersion); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack country code */ + rv = UnpackPDU(aPDU, aOut.mCountryCode); + if (NS_FAILED(rv)) { + return rv; + } + /* unpack descriptor length */ + rv = UnpackPDU(aPDU, aOut.mDescriptorLength); + if (NS_FAILED(rv)) { + return rv; + } + if (MOZ_HAL_IPC_PACK_WARN_IF( + aOut.mDescriptorLength > BLUETOOTH_HID_MAX_DESC_LEN, + BluetoothHidInfoParam)) { + return NS_ERROR_ILLEGAL_VALUE; + } + /* unpack descriptor value */ + return UnpackPDU( + aPDU, + UnpackArray(aOut.mDescriptorValue, aOut.mDescriptorLength)); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidReport& aOut) +{ + return UnpackPDU(aPDU, aOut.mReportData); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidProtocolMode& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidConnectionState& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidStatus& aOut) +{ + return UnpackPDU( + aPDU, UnpackConversion(aOut)); +} + END_BLUETOOTH_NAMESPACE diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h index ec743c554410..d06a3b3822e4 100644 --- a/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h +++ b/dom/bluetooth/bluedroid/BluetoothDaemonHelpers.h @@ -173,6 +173,15 @@ Convert(uint8_t aIn, BluetoothStatus& aOut); nsresult Convert(int32_t aIn, BluetoothAttributeHandle& aOut); +nsresult +Convert(uint8_t aIn, BluetoothHidProtocolMode& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothHidConnectionState& aOut); + +nsresult +Convert(uint8_t aIn, BluetoothHidStatus& aOut); + nsresult Convert(int32_t aIn, BluetoothGattStatus& aOut); @@ -257,6 +266,12 @@ Convert(BluetoothGattWriteType aIn, int32_t& aOut); nsresult Convert(nsresult aIn, BluetoothStatus& aOut); +nsresult +Convert(BluetoothHidProtocolMode aIn, uint8_t& aOut); + +nsresult +Convert(BluetoothHidReportType aIn, uint8_t& aOut); + // // Packing // @@ -371,6 +386,18 @@ PackPDU(BluetoothGattWriteType aIn, DaemonSocketPDU& aPDU); nsresult PackPDU(BluetoothTransport aIn, DaemonSocketPDU& aPDU); +nsresult +PackPDU(const BluetoothHidInfoParam& aIn, DaemonSocketPDU& aPDU); + +nsresult +PackPDU(const BluetoothHidReport& aIn, DaemonSocketPDU& aPDU); + +nsresult +PackPDU(BluetoothHidProtocolMode aIn, DaemonSocketPDU& aPDU); + +nsresult +PackPDU(BluetoothHidReportType aIn, DaemonSocketPDU& aPDU); + /* This implementation of |PackPDU| packs |BluetoothUuid| in reversed order. * (ex. reversed GATT UUID, see bug 1171866) */ @@ -513,6 +540,21 @@ UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattWriteParam& aOut); nsresult UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattNotifyParam& aOut); +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidInfoParam& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidReport& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidProtocolMode& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidConnectionState& aOut); + +nsresult +UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidStatus& aOut); + /* This implementation of |UnpackPDU| unpacks |BluetoothUuid| in reversed * order. (ex. reversed GATT UUID, see bug 1171866) */ diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp new file mode 100644 index 000000000000..d9ba6970ef24 --- /dev/null +++ b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp @@ -0,0 +1,677 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "BluetoothDaemonHidInterface.h" +#include "mozilla/unused.h" + +BEGIN_BLUETOOTH_NAMESPACE + +using namespace mozilla::ipc; + +// +// Hid module +// + +BluetoothHidNotificationHandler* + BluetoothDaemonHidModule::sNotificationHandler; + +void +BluetoothDaemonHidModule::SetNotificationHandler( + BluetoothHidNotificationHandler* aNotificationHandler) +{ + sNotificationHandler = aNotificationHandler; +} + +void +BluetoothDaemonHidModule::HandleSvc( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes) +{ + static void (BluetoothDaemonHidModule::* const HandleOp[])( + const DaemonSocketPDUHeader&, DaemonSocketPDU&, + DaemonSocketResultHandler*) = { + [0] = &BluetoothDaemonHidModule::HandleRsp, + [1] = &BluetoothDaemonHidModule::HandleNtf + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + // Negate twice to map bit to 0/1 + unsigned long isNtf = !!(aHeader.mOpcode & 0x80); + + (this->*(HandleOp[isNtf]))(aHeader, aPDU, aRes); +} + +// Commands +// + +nsresult +BluetoothDaemonHidModule::ConnectCmd( + const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_CONNECT, 6)); // Address + + nsresult rv = PackPDU(aRemoteAddr, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::DisconnectCmd( + const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_DISCONNECT, 6)); // Address + + nsresult rv = PackPDU(aRemoteAddr, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::VirtualUnplugCmd( + const BluetoothAddress& aRemoteAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_VIRTUAL_UNPLUG, 6)); // Address + + nsresult rv = PackPDU(aRemoteAddr, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::SetInfoCmd( + const BluetoothAddress& aRemoteAddr, + const BluetoothHidInfoParam& aHidInfoParam, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_INFO, 0)); + + nsresult rv = PackPDU(aRemoteAddr, aHidInfoParam, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::GetProtocolCmd( + const BluetoothAddress& aRemoteAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_PROTOCOL, + 6 + // Address + 1)); // Protocol Mode + + nsresult rv = PackPDU(aRemoteAddr, aHidProtocolMode, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::SetProtocolCmd( + const BluetoothAddress& aRemoteAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_PROTOCOL, + 6 + // Remote Address + 1)); // Protocol Mode + + nsresult rv = PackPDU(aRemoteAddr, aHidProtocolMode, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::GetReportCmd( + const BluetoothAddress& aRemoteAddr, BluetoothHidReportType aType, + uint8_t aReportId, uint16_t aBuffSize, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_GET_REPORT, + 6 + // Address + 1 + // Report Type + 1 + // Report ID + 2)); // Buffer Size + + nsresult rv = PackPDU(aRemoteAddr, aType, aReportId, aBuffSize, *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::SetReportCmd( + const BluetoothAddress& aRemoteAddr, BluetoothHidReportType aType, + const BluetoothHidReport& aReport, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_SET_REPORT, 0)); + + nsresult rv = PackPDU(aRemoteAddr, aType, aReport, *pdu); + + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +nsresult +BluetoothDaemonHidModule::SendDataCmd( + const BluetoothAddress& aRemoteAddr, uint16_t aDataLen, const uint8_t* aData, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoPtr pdu( + new DaemonSocketPDU(SERVICE_ID, OPCODE_SEND_DATA, 0)); + + nsresult rv = PackPDU(aRemoteAddr, aDataLen, + PackArray(aData, aDataLen), *pdu); + if (NS_FAILED(rv)) { + return rv; + } + rv = Send(pdu, aRes); + if (NS_FAILED(rv)) { + return rv; + } + Unused << pdu.forget(); + return NS_OK; +} + +// Responses +// + +void +BluetoothDaemonHidModule::ErrorRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ErrorRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::OnError, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::ConnectRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::Connect, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::DisconnectRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::Disconnect, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::VirtualUnplugRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::VirtualUnplug, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::SetInfoRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::SetInfo, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::GetProtocolRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::GetProtocol, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::SetProtocolRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::SetProtocol, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::GetReportRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::GetReport, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::SetReportRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::SetReport, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::SendDataRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, BluetoothHidResultHandler* aRes) +{ + ResultRunnable::Dispatch( + aRes, &BluetoothHidResultHandler::SendData, UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::HandleRsp( + const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes) +{ + static void (BluetoothDaemonHidModule::* const HandleRsp[])( + const DaemonSocketPDUHeader&, + DaemonSocketPDU&, + BluetoothHidResultHandler*) = { + [OPCODE_ERROR] = &BluetoothDaemonHidModule::ErrorRsp, + [OPCODE_CONNECT] = &BluetoothDaemonHidModule::ConnectRsp, + [OPCODE_DISCONNECT] = &BluetoothDaemonHidModule::DisconnectRsp, + [OPCODE_VIRTUAL_UNPLUG] = &BluetoothDaemonHidModule::VirtualUnplugRsp, + [OPCODE_SET_INFO] = &BluetoothDaemonHidModule::SetInfoRsp, + [OPCODE_GET_PROTOCOL] = &BluetoothDaemonHidModule::GetProtocolRsp, + [OPCODE_SET_PROTOCOL] = &BluetoothDaemonHidModule::SetProtocolRsp, + [OPCODE_GET_REPORT] = &BluetoothDaemonHidModule::GetReportRsp, + [OPCODE_SET_REPORT] = &BluetoothDaemonHidModule::SetReportRsp, + [OPCODE_SEND_DATA] = &BluetoothDaemonHidModule::SendDataRsp + }; + + MOZ_ASSERT(!NS_IsMainThread()); // I/O thread + + if (NS_WARN_IF(!(aHeader.mOpcode < MOZ_ARRAY_LENGTH(HandleRsp))) || + NS_WARN_IF(!HandleRsp[aHeader.mOpcode])) { + return; + } + + RefPtr res = + static_cast(aRes); + + if (!res) { + return; // Return early if no result handler has been set for response + } + + (this->*(HandleRsp[aHeader.mOpcode]))(aHeader, aPDU, res); +} + +// Notifications +// + +// Returns the current notification handler to a notification runnable +class BluetoothDaemonHidModule::NotificationHandlerWrapper final +{ +public: + typedef BluetoothHidNotificationHandler ObjectType; + + static ObjectType* GetInstance() + { + MOZ_ASSERT(NS_IsMainThread()); + + return sNotificationHandler; + } +}; + +void +BluetoothDaemonHidModule::ConnectionStateNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + ConnectionStateNotification::Dispatch( + &BluetoothHidNotificationHandler::ConnectionStateNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::HidInfoNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + HidInfoNotification::Dispatch( + &BluetoothHidNotificationHandler::HidInfoNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::ProtocolModeNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + ProtocolModeNotification::Dispatch( + &BluetoothHidNotificationHandler::ProtocolModeNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::IdleTimeNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + IdleTimeNotification::Dispatch( + &BluetoothHidNotificationHandler::IdleTimeNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::GetReportNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + GetReportNotification::Dispatch( + &BluetoothHidNotificationHandler::GetReportNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::VirtualUnplugNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + VirtualUnplugNotification::Dispatch( + &BluetoothHidNotificationHandler::VirtualUnplugNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::HandshakeNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU) +{ + HandshakeNotification::Dispatch( + &BluetoothHidNotificationHandler::HandshakeNotification, + UnpackPDUInitOp(aPDU)); +} + +void +BluetoothDaemonHidModule::HandleNtf( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes) +{ + static void (BluetoothDaemonHidModule::* const HandleNtf[])( + const DaemonSocketPDUHeader&, DaemonSocketPDU&) = { + [0] = &BluetoothDaemonHidModule::ConnectionStateNtf, + [1] = &BluetoothDaemonHidModule::HidInfoNtf, + [2] = &BluetoothDaemonHidModule::ProtocolModeNtf, + [3] = &BluetoothDaemonHidModule::IdleTimeNtf, + [4] = &BluetoothDaemonHidModule::GetReportNtf, + [5] = &BluetoothDaemonHidModule::VirtualUnplugNtf, + [6] = &BluetoothDaemonHidModule::HandshakeNtf + }; + + MOZ_ASSERT(!NS_IsMainThread()); + + uint8_t index = aHeader.mOpcode - 0x81; + + if (NS_WARN_IF(!(index < MOZ_ARRAY_LENGTH(HandleNtf))) || + NS_WARN_IF(!HandleNtf[index])) { + return; + } + + (this->*(HandleNtf[index]))(aHeader, aPDU); +} + +// +// Hid Interface +// + +BluetoothDaemonHidInterface::BluetoothDaemonHidInterface( + BluetoothDaemonHidModule* aModule) + : mModule(aModule) +{ } + +BluetoothDaemonHidInterface::~BluetoothDaemonHidInterface() +{ } + +void +BluetoothDaemonHidInterface::SetNotificationHandler( + BluetoothHidNotificationHandler* aNotificationHandler) +{ + MOZ_ASSERT(mModule); + + mModule->SetNotificationHandler(aNotificationHandler); +} + +/* Connect / Disconnect */ + +void +BluetoothDaemonHidInterface::Connect( + const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->ConnectCmd(aBdAddr, aRes); + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +void +BluetoothDaemonHidInterface::Disconnect( + const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes); + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +/* Virtual Unplug */ + +void +BluetoothDaemonHidInterface::VirtualUnplug( + const BluetoothAddress& aBdAddr, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->VirtualUnplugCmd(aBdAddr, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +/* Set Info */ + +void +BluetoothDaemonHidInterface::SetInfo( + const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->SetInfoCmd(aBdAddr, aHidInfoParam, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +/* Protocol */ + +void +BluetoothDaemonHidInterface::GetProtocol( + const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->GetProtocolCmd(aBdAddr, aHidProtocolMode, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +void +BluetoothDaemonHidInterface::SetProtocol( + const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->SetProtocolCmd(aBdAddr, aHidProtocolMode, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +/* Report */ + +void +BluetoothDaemonHidInterface::GetReport( + const BluetoothAddress& aBdAddr, BluetoothHidReportType aType, + uint8_t aReportId, uint16_t aBuffSize, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->GetReportCmd( + aBdAddr, aType, aReportId, aBuffSize, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +void +BluetoothDaemonHidInterface::SetReport( + const BluetoothAddress& aBdAddr, BluetoothHidReportType aType, + const BluetoothHidReport& aReport, BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->SetReportCmd( + aBdAddr, aType, aReport, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +/* Send Data */ + +void +BluetoothDaemonHidInterface::SendData( + const BluetoothAddress& aBdAddr, uint16_t aDataLen, const uint8_t* aData, + BluetoothHidResultHandler* aRes) +{ + MOZ_ASSERT(mModule); + + nsresult rv = mModule->SendDataCmd(aBdAddr, aDataLen, aData, aRes); + + if (NS_FAILED(rv)) { + DispatchError(aRes, rv); + } +} + +void +BluetoothDaemonHidInterface::DispatchError( + BluetoothHidResultHandler* aRes, BluetoothStatus aStatus) +{ + DaemonResultRunnable1::Dispatch( + aRes, &BluetoothHidResultHandler::OnError, + ConstantInitOp1(aStatus)); +} + +void +BluetoothDaemonHidInterface::DispatchError( + BluetoothHidResultHandler* aRes, nsresult aRv) +{ + BluetoothStatus status; + + if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) { + status = STATUS_FAIL; + } + DispatchError(aRes, status); +} + +END_BLUETOOTH_NAMESPACE diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h new file mode 100644 index 000000000000..0d2e3365b28f --- /dev/null +++ b/dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h +#define mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h + +#include "BluetoothDaemonHelpers.h" +#include "BluetoothInterface.h" +#include "mozilla/ipc/DaemonRunnables.h" + +BEGIN_BLUETOOTH_NAMESPACE + +using mozilla::ipc::DaemonSocketPDU; +using mozilla::ipc::DaemonSocketPDUHeader; +using mozilla::ipc::DaemonSocketResultHandler; + +class BluetoothDaemonHidModule +{ +public: + enum { + SERVICE_ID = 0x03 + }; + + enum { + OPCODE_ERROR = 0x00, + OPCODE_CONNECT = 0x01, + OPCODE_DISCONNECT = 0x02, + OPCODE_VIRTUAL_UNPLUG = 0x03, + OPCODE_SET_INFO = 0x04, + OPCODE_GET_PROTOCOL = 0x05, + OPCODE_SET_PROTOCOL = 0x06, + OPCODE_GET_REPORT = 0x07, + OPCODE_SET_REPORT = 0x08, + OPCODE_SEND_DATA = 0x09 + }; + + virtual nsresult Send(DaemonSocketPDU* aPDU, + DaemonSocketResultHandler* aRes) = 0; + + void SetNotificationHandler( + BluetoothHidNotificationHandler* aNotificationHandler); + + // + // Commands + // + + nsresult ConnectCmd(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes); + nsresult DisconnectCmd(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes); + + /* Virtual Unplug */ + + nsresult VirtualUnplugCmd(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes); + + /* Set Info */ + + nsresult SetInfoCmd( + const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam, + BluetoothHidResultHandler* aRes); + + /* Protocol */ + + nsresult GetProtocolCmd(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes); + nsresult SetProtocolCmd(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes); + + /* Report */ + + nsresult GetReportCmd(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + uint8_t aReportId, + uint16_t aBuffSize, + BluetoothHidResultHandler* aRes); + nsresult SetReportCmd(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + const BluetoothHidReport& aReport, + BluetoothHidResultHandler* aRes); + + /* Send Data */ + + nsresult SendDataCmd(const BluetoothAddress& aBdAddr, + uint16_t aDataLen, const uint8_t* aData, + BluetoothHidResultHandler* aRes); + +protected: + void HandleSvc(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes); + + // + // Responses + // + + typedef mozilla::ipc::DaemonResultRunnable0< + BluetoothHidResultHandler, void> + ResultRunnable; + + typedef mozilla::ipc::DaemonResultRunnable1< + BluetoothHidResultHandler, void, BluetoothStatus, BluetoothStatus> + ErrorRunnable; + + void ErrorRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void ConnectRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void DisconnectRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void VirtualUnplugRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void SetInfoRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void GetProtocolRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void SetProtocolRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void GetReportRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void SetReportRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void SendDataRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + BluetoothHidResultHandler* aRes); + + void HandleRsp(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes); + + // + // Notifications + // + + class NotificationHandlerWrapper; + + typedef mozilla::ipc::DaemonNotificationRunnable2< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidConnectionState, + const BluetoothAddress&, BluetoothHidConnectionState> + ConnectionStateNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable2< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidInfoParam, + const BluetoothAddress&, const BluetoothHidInfoParam&> + HidInfoNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable3< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidStatus, BluetoothHidProtocolMode, + const BluetoothAddress&, BluetoothHidStatus, BluetoothHidProtocolMode> + ProtocolModeNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable3< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidStatus, uint16_t, + const BluetoothAddress&, BluetoothHidStatus, uint16_t> + IdleTimeNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable3< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidStatus, BluetoothHidReport, + const BluetoothAddress&, BluetoothHidStatus, + const BluetoothHidReport&> + GetReportNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable2< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidStatus, + const BluetoothAddress&, BluetoothHidStatus> + VirtualUnplugNotification; + + typedef mozilla::ipc::DaemonNotificationRunnable2< + NotificationHandlerWrapper, void, + BluetoothAddress, BluetoothHidStatus, + const BluetoothAddress&, BluetoothHidStatus> + HandshakeNotification; + + void ConnectionStateNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void HidInfoNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void ProtocolModeNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void IdleTimeNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void GetReportNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void VirtualUnplugNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void HandshakeNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU); + + void HandleNtf(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes); + + static BluetoothHidNotificationHandler* sNotificationHandler; +}; + +class BluetoothDaemonHidInterface final + : public BluetoothHidInterface +{ +public: + BluetoothDaemonHidInterface(BluetoothDaemonHidModule* aModule); + ~BluetoothDaemonHidInterface(); + + void SetNotificationHandler( + BluetoothHidNotificationHandler* aNotificationHandler) override; + + /* Connect / Disconnect */ + + void Connect(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) override; + void Disconnect(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) override; + + /* Virtual Unplug */ + + void VirtualUnplug(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) override; + + /* Set Info */ + + void SetInfo( + const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam, + BluetoothHidResultHandler* aRes) override; + + /* Protocol */ + + void GetProtocol(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtoclMode, + BluetoothHidResultHandler* aRes) override; + + void SetProtocol(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes) override; + + /* Report */ + + void GetReport(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + uint8_t aReportId, uint16_t aBuffSize, + BluetoothHidResultHandler* aRes) override; + void SetReport(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + const BluetoothHidReport& aReport, + BluetoothHidResultHandler* aRes) override; + + /* Send Data */ + + void SendData(const BluetoothAddress& aBdAddr, + uint16_t aDataLen, const uint8_t* aData, + BluetoothHidResultHandler* aRes) override; + +private: + void DispatchError(BluetoothHidResultHandler* aRes, + BluetoothStatus aStatus); + void DispatchError(BluetoothHidResultHandler* aRes, nsresult aRv); + + BluetoothDaemonHidModule* mModule; +}; + +END_BLUETOOTH_NAMESPACE + +#endif // mozilla_dom_bluetooth_bluedroid_BluetoothDaemonHidInterface_h diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp index 74a96184dd73..c23dedb91128 100644 --- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp +++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.cpp @@ -14,6 +14,7 @@ #include "BluetoothDaemonGattInterface.h" #include "BluetoothDaemonHandsfreeInterface.h" #include "BluetoothDaemonHelpers.h" +#include "BluetoothDaemonHidInterface.h" #include "BluetoothDaemonSetupInterface.h" #include "BluetoothDaemonSocketInterface.h" #include "mozilla/Hal.h" @@ -80,6 +81,7 @@ class BluetoothDaemonProtocol final , public BluetoothDaemonA2dpModule , public BluetoothDaemonAvrcpModule , public BluetoothDaemonGattModule + , public BluetoothDaemonHidModule { public: BluetoothDaemonProtocol(); @@ -124,6 +126,9 @@ private: void HandleGattSvc(const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU, DaemonSocketResultHandler* aRes); + void HandleHidSvc(const DaemonSocketPDUHeader& aHeader, + DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes); DaemonSocket* mConnection; nsTArray> mResQ; @@ -215,6 +220,14 @@ BluetoothDaemonProtocol::HandleGattSvc( BluetoothDaemonGattModule::HandleSvc(aHeader, aPDU, aRes); } +void +BluetoothDaemonProtocol::HandleHidSvc( + const DaemonSocketPDUHeader& aHeader, DaemonSocketPDU& aPDU, + DaemonSocketResultHandler* aRes) +{ + BluetoothDaemonHidModule::HandleSvc(aHeader, aPDU, aRes); +} + void BluetoothDaemonProtocol::Handle(DaemonSocketPDU& aPDU) { @@ -227,7 +240,8 @@ BluetoothDaemonProtocol::Handle(DaemonSocketPDU& aPDU) &BluetoothDaemonProtocol::HandleCoreSvc, [BluetoothDaemonSocketModule::SERVICE_ID] = &BluetoothDaemonProtocol::HandleSocketSvc, - [0x03] = nullptr, // HID host + [BluetoothDaemonHidModule::SERVICE_ID] = + &BluetoothDaemonProtocol::HandleHidSvc, [0x04] = nullptr, // PAN [BluetoothDaemonHandsfreeModule::SERVICE_ID] = &BluetoothDaemonProtocol::HandleHandsfreeSvc, @@ -634,6 +648,18 @@ BluetoothDaemonInterface::GetBluetoothGattInterface() return mGattInterface; } +BluetoothHidInterface* +BluetoothDaemonInterface::GetBluetoothHidInterface() +{ + if (mHidInterface) { + return mHidInterface; + } + + mHidInterface = new BluetoothDaemonHidInterface(mProtocol); + + return mHidInterface; +} + // |DaemonSocketConsumer|, |ListenSocketConsumer| void diff --git a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h index eed3e8afadc6..8d0689b13dd9 100644 --- a/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h +++ b/dom/bluetooth/bluedroid/BluetoothDaemonInterface.h @@ -30,6 +30,7 @@ class BluetoothDaemonHandsfreeInterface; class BluetoothDaemonProtocol; class BluetoothDaemonSetupInterface; class BluetoothDaemonSocketInterface; +class BluetoothDaemonHidInterface; class BluetoothDaemonInterface final : public BluetoothInterface @@ -58,6 +59,7 @@ public: BluetoothA2dpInterface* GetBluetoothA2dpInterface() override; BluetoothAvrcpInterface* GetBluetoothAvrcpInterface() override; BluetoothGattInterface* GetBluetoothGattInterface() override; + BluetoothHidInterface* GetBluetoothHidInterface() override; protected: enum Channel { @@ -97,6 +99,7 @@ private: nsAutoPtr mA2dpInterface; nsAutoPtr mAvrcpInterface; nsAutoPtr mGattInterface; + nsAutoPtr mHidInterface; }; END_BLUETOOTH_NAMESPACE diff --git a/dom/bluetooth/bluedroid/BluetoothHidManager.cpp b/dom/bluetooth/bluedroid/BluetoothHidManager.cpp new file mode 100644 index 000000000000..954bf5b263f1 --- /dev/null +++ b/dom/bluetooth/bluedroid/BluetoothHidManager.cpp @@ -0,0 +1,610 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "BluetoothHidManager.h" +#include "BluetoothService.h" +#include "BluetoothUtils.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "nsIObserverService.h" + +using namespace mozilla; +USING_BLUETOOTH_NAMESPACE + +namespace { + StaticRefPtr sBluetoothHidManager; + static BluetoothHidInterface* sBluetoothHidInterface = nullptr; + bool sInShutdown = false; +} // namesapce + +const int BluetoothHidManager::MAX_NUM_CLIENTS = 1; + +BluetoothHidManager::BluetoothHidManager() + : mHidConnected(false) +{ +} + +void +BluetoothHidManager::Reset() +{ + mDeviceAddress.Clear(); + mController = nullptr; + mHidConnected = false; +} + +class BluetoothHidManager::RegisterModuleResultHandler final + : public BluetoothSetupResultHandler +{ +public: + RegisterModuleResultHandler(BluetoothHidInterface* aInterface, + BluetoothProfileResultHandler* aRes) + : mInterface(aInterface) + , mRes(aRes) + { + MOZ_ASSERT(mInterface); + } + + void OnError(BluetoothStatus aStatus) override + { + MOZ_ASSERT(NS_IsMainThread()); + + BT_WARNING("BluetoothSetupInterface::RegisterModule failed for HID: %d", + (int)aStatus); + + mInterface->SetNotificationHandler(nullptr); + + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void RegisterModule() override + { + MOZ_ASSERT(NS_IsMainThread()); + + sBluetoothHidInterface = mInterface; + + if (mRes) { + mRes->Init(); + } + } + +private: + BluetoothHidInterface* mInterface; + RefPtr mRes; +}; + +class BluetoothHidManager::InitProfileResultHandlerRunnable final + : public nsRunnable +{ +public: + InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes, + nsresult aRv) + : mRes(aRes) + , mRv(aRv) + { + MOZ_ASSERT(mRes); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_SUCCEEDED(mRv)) { + mRes->Init(); + } else { + mRes->OnError(mRv); + } + return NS_OK; + } + +private: + RefPtr mRes; + nsresult mRv; +}; + +// static +void +BluetoothHidManager::InitHidInterface(BluetoothProfileResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (sBluetoothHidInterface) { + BT_LOGR("Bluetooth HID interface is already initialized."); + RefPtr r = + new InitProfileResultHandlerRunnable(aRes, NS_OK); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID Init runnable"); + } + return; + } + + auto btInf = BluetoothInterface::GetInstance(); + + if (NS_WARN_IF(!btInf)) { + // If there's no backend interface, we dispatch a runnable + // that calls the profile result handler. + RefPtr r = + new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID OnError runnable"); + } + return; + } + + auto setupInterface = btInf->GetBluetoothSetupInterface(); + + if (NS_WARN_IF(!setupInterface)) { + // If there's no Setup interface, we dispatch a runnable + // that calls the profile result handler. + RefPtr r = + new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID OnError runnable"); + } + return; + } + + auto hidinterface = btInf->GetBluetoothHidInterface(); + + if (NS_WARN_IF(!hidinterface)) { + // If there's no HID interface, we dispatch a runnable + // that calls the profile result handler. + RefPtr r = + new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID OnError runnable"); + } + return; + } + + // Set notification handler _before_ registering the module. It could + // happen that we receive notifications, before the result handler runs. + hidinterface->SetNotificationHandler(BluetoothHidManager::Get()); + + setupInterface->RegisterModule( + SETUP_SERVICE_ID_HID, 0, MAX_NUM_CLIENTS, + new RegisterModuleResultHandler(hidinterface, aRes)); +} + +BluetoothHidManager::~BluetoothHidManager() +{ } + +void +BluetoothHidManager::Uninit() +{ + nsCOMPtr obs = services::GetObserverService(); + NS_ENSURE_TRUE_VOID(obs); + + if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) { + BT_WARNING("Failed to remove shutdown observer!"); + } +} + +class BluetoothHidManager::UnregisterModuleResultHandler final + : public BluetoothSetupResultHandler +{ +public: + UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + void OnError(BluetoothStatus aStatus) override + { + MOZ_ASSERT(NS_IsMainThread()); + + BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for HID: %d", + (int)aStatus); + + sBluetoothHidInterface->SetNotificationHandler(nullptr); + sBluetoothHidInterface = nullptr; + + sBluetoothHidManager->Uninit(); + sBluetoothHidManager = nullptr; + + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void UnregisterModule() override + { + MOZ_ASSERT(NS_IsMainThread()); + + sBluetoothHidInterface->SetNotificationHandler(nullptr); + sBluetoothHidInterface = nullptr; + + sBluetoothHidManager->Uninit(); + sBluetoothHidManager = nullptr; + + if (mRes) { + mRes->Deinit(); + } + } + +private: + RefPtr mRes; +}; + +class BluetoothHidManager::DeinitProfileResultHandlerRunnable final + : public nsRunnable +{ +public: + DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes, + nsresult aRv) + : mRes(aRes) + , mRv(aRv) + { + MOZ_ASSERT(mRes); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_SUCCEEDED(mRv)) { + mRes->Deinit(); + } else { + mRes->OnError(mRv); + } + return NS_OK; + } + +private: + RefPtr mRes; + nsresult mRv; +}; + +// static +void +BluetoothHidManager::DeinitHidInterface(BluetoothProfileResultHandler* aRes) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!sBluetoothHidInterface) { + BT_LOGR("Bluetooth Hid interface has not been initialized."); + RefPtr r = + new DeinitProfileResultHandlerRunnable(aRes, NS_OK); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID Deinit runnable"); + } + return; + } + + auto btInf = BluetoothInterface::GetInstance(); + + if (NS_WARN_IF(!btInf)) { + // If there's no backend interface, we dispatch a runnable + // that calls the profile result handler. + RefPtr r = + new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID OnError runnable"); + } + return; + } + + auto setupInterface = btInf->GetBluetoothSetupInterface(); + + if (NS_WARN_IF(!setupInterface)) { + // If there's no Setup interface, we dispatch a runnable + // that calls the profile result handler. + RefPtr r = + new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch HID OnError runnable"); + } + return; + } + + setupInterface->UnregisterModule( + SETUP_SERVICE_ID_HID, + new UnregisterModuleResultHandler(aRes)); +} + +NS_IMETHODIMP +BluetoothHidManager::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(sBluetoothHidManager); + + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + HandleShutdown(); + return NS_OK; + } + + MOZ_ASSERT(false, "BluetoothHidManager got unexpected topic!"); + return NS_ERROR_UNEXPECTED; +} + +// static +BluetoothHidManager* +BluetoothHidManager::Get() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // If we already exist, exit early + if (sBluetoothHidManager) { + return sBluetoothHidManager; + } + + // If we're in shutdown, don't create a new instance + NS_ENSURE_FALSE(sInShutdown, nullptr); + + // Create a new instance, register, and return + sBluetoothHidManager = new BluetoothHidManager(); + + return sBluetoothHidManager; +} + +void +BluetoothHidManager::NotifyConnectionStateChanged(const nsAString& aType) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Notify Gecko observers + nsCOMPtr obs = services::GetObserverService(); + NS_ENSURE_TRUE_VOID(obs); + + nsAutoString deviceAddressStr; + AddressToString(mDeviceAddress, deviceAddressStr); + + if (NS_FAILED(obs->NotifyObservers(this, NS_ConvertUTF16toUTF8(aType).get(), + deviceAddressStr.get()))) { + BT_WARNING("Failed to notify observsers!"); + } + + // Dispatch an event of status change + DispatchStatusChangedEvent( + NS_LITERAL_STRING(HID_STATUS_CHANGED_ID), mDeviceAddress, IsConnected()); +} + +bool +BluetoothHidManager::IsConnected() +{ + return mHidConnected; +} + +void +BluetoothHidManager::OnConnectError() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); + Reset(); +} + +class BluetoothHidManager::ConnectResultHandler final + : public BluetoothHidResultHandler +{ +public: + ConnectResultHandler(BluetoothHidManager* aHidManager) + : mHidManager(aHidManager) + { + MOZ_ASSERT(mHidManager); + } + + void OnError(BluetoothStatus aStatus) override + { + BT_WARNING("BluetoothHidInterface::Connect failed: %d", + (int)aStatus); + mHidManager->OnConnectError(); + } + +private: + BluetoothHidManager* mHidManager; +}; + +void +BluetoothHidManager::Connect(const BluetoothAddress& aDeviceAddress, + BluetoothProfileController* aController) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aDeviceAddress.IsCleared()); + MOZ_ASSERT(aController && !mController); + + if(sInShutdown) { + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); + return; + } + + if(IsConnected()) { + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED)); + return; + } + + if(!sBluetoothHidInterface) { + BT_LOGR("sBluetoothHidInterface is null"); + aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); + return; + } + + mDeviceAddress = aDeviceAddress; + mController = aController; + + sBluetoothHidInterface->Connect(mDeviceAddress, + new ConnectResultHandler(this)); +} + +void +BluetoothHidManager::OnDisconnectError() +{ + MOZ_ASSERT(NS_IsMainThread()); + NS_ENSURE_TRUE_VOID(mController); + + mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); + + mController = nullptr; +} + +class BluetoothHidManager::DisconnectResultHandler final + : public BluetoothHidResultHandler +{ +public: + DisconnectResultHandler(BluetoothHidManager* aHidManager) + : mHidManager(aHidManager) + { + MOZ_ASSERT(mHidManager); + } + + void OnError(BluetoothStatus aStatus) override + { + BT_WARNING("BluetoothHidInterface::Disconnect failed: %d", + (int)aStatus); + mHidManager->OnDisconnectError(); + } + +private: + BluetoothHidManager* mHidManager; +}; + +void +BluetoothHidManager::Disconnect(BluetoothProfileController* aController) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mController); + + if (!IsConnected()) { + if (aController) { + aController->NotifyCompletion( + NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED)); + } + return; + } + + MOZ_ASSERT(!mDeviceAddress.IsCleared()); + + if (!sBluetoothHidInterface) { + BT_LOGR("sBluetoothHidInterface is null"); + if (aController) { + aController->NotifyCompletion( + NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE)); + } + return; + } + + mController = aController; + + sBluetoothHidInterface->Disconnect(mDeviceAddress, + new DisconnectResultHandler(this)); +} + +void +BluetoothHidManager::OnConnect(const nsAString& aErrorStr) +{ + MOZ_ASSERT(NS_IsMainThread()); + + /** + * On the one hand, notify the controller that we've done for outbound + * connections. On the other hand, we do nothing for inbound connections. + */ + NS_ENSURE_TRUE_VOID(mController); + + mController->NotifyCompletion(aErrorStr); + mController = nullptr; +} + +void +BluetoothHidManager::OnDisconnect(const nsAString& aErrorStr) +{ + MOZ_ASSERT(NS_IsMainThread()); + + /** + * On the one hand, notify the controller that we've done for outbound + * connections. On the other hand, we do nothing for inbound connections. + */ + NS_ENSURE_TRUE_VOID(mController); + + mController->NotifyCompletion(aErrorStr); + Reset(); +} + +void +BluetoothHidManager::HandleShutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + sInShutdown = true; + Disconnect(nullptr); + sBluetoothHidManager = nullptr; +} + +void +BluetoothHidManager::HandleBackendError() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mHidConnected) { + ConnectionStateNotification(mDeviceAddress, + HID_CONNECTION_STATE_DISCONNECTED); + } +} + +void +BluetoothHidManager::GetAddress(BluetoothAddress& aDeviceAddress) +{ + aDeviceAddress = mDeviceAddress; +} + +void +BluetoothHidManager::OnUpdateSdpRecords( + const BluetoothAddress& aDeviceAddress) +{ + // Bluedroid handles this part + MOZ_ASSERT(false); +} + +void +BluetoothHidManager::OnGetServiceChannel( + const BluetoothAddress& aDeviceAddress, + const BluetoothUuid& aServiceUuid, + int aChannel) +{ + // Bluedroid handles this part + MOZ_ASSERT(false); +} + +// +// Bluetooth notifications +// + +/** + * There are totally 10 connection states, and will receive 4 possible + * states: "connected", "connecting", "disconnected", "disconnecting". + * Here we only handle connected and disconnected states. We do nothing + * for remaining states. + * + * Possible cases are listed below: + * CONNECTED: + * 1. Successful inbound or outbound connection + * DISCONNECTED: + * 2. Attempt disconnection succeeded + * 3. Attempt connection failed + * 4. Either unpair from the remote device or the remote device is + * out of range in connected state + */ +void +BluetoothHidManager::ConnectionStateNotification( + const BluetoothAddress& aBdAddr, BluetoothHidConnectionState aState) +{ + MOZ_ASSERT(NS_IsMainThread()); + BT_LOGR("state %d", aState); + + if (aState == HID_CONNECTION_STATE_CONNECTED) { + mHidConnected = true; + mDeviceAddress = aBdAddr; + NotifyConnectionStateChanged( + NS_LITERAL_STRING(BLUETOOTH_HID_STATUS_CHANGED_ID)); + OnConnect(EmptyString()); + } else if (aState == HID_CONNECTION_STATE_DISCONNECTED) { + mHidConnected = false; + NotifyConnectionStateChanged( + NS_LITERAL_STRING(BLUETOOTH_HID_STATUS_CHANGED_ID)); + OnDisconnect(EmptyString()); + } +} + +NS_IMPL_ISUPPORTS(BluetoothHidManager, nsIObserver) diff --git a/dom/bluetooth/bluedroid/BluetoothHidManager.h b/dom/bluetooth/bluedroid/BluetoothHidManager.h new file mode 100644 index 000000000000..37ae363f0a08 --- /dev/null +++ b/dom/bluetooth/bluedroid/BluetoothHidManager.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_bluetooth_bluedroid_BluetoothHidManager_h +#define mozilla_dom_bluetooth_bluedroid_BluetoothHidMnagaer_h + +#include "BluetoothCommon.h" +#include "BluetoothInterface.h" +#include "BluetoothProfileController.h" +#include "BluetoothProfileManagerBase.h" + +BEGIN_BLUETOOTH_NAMESPACE + +class BluetoothHidManager : public BluetoothProfileManagerBase + , public BluetoothHidNotificationHandler +{ +public: + BT_DECL_PROFILE_MGR_BASE + + static const int MAX_NUM_CLIENTS; + + void OnConnectError(); + void OnDisconnectError(); + + virtual void GetName(nsACString& aName) + { + aName.AssignLiteral("HID"); + } + + static BluetoothHidManager* Get(); + static void InitHidInterface(BluetoothProfileResultHandler* aRes); + static void DeinitHidInterface(BluetoothProfileResultHandler* aRes); + + void HandleBackendError(); + +protected: + virtual ~BluetoothHidManager(); + +private: + class DeinitProfileResultHandlerRunnable; + class InitProfileResultHandlerRunnable; + class RegisterModuleResultHandler; + class UnregisterModuleResultHandler; + + class ConnectResultHandler; + class DisconnectResultHandler; + + BluetoothHidManager(); + void Uninit(); + void HandleShutdown(); + void NotifyConnectionStateChanged(const nsAString& aType); + + // + // Bluetooth notifications + // + + void ConnectionStateNotification( + const BluetoothAddress& aBdAddr, + BluetoothHidConnectionState aState) override; + + bool mHidConnected; + BluetoothAddress mDeviceAddress; + RefPtr mController; +}; + +END_BLUETOOTH_NAMESPACE + +#endif // mozilla_dom_bluetooth_bluedroid_BluetoothHidManager_h diff --git a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp index b733c921791b..720881ca9280 100644 --- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp +++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp @@ -2632,6 +2632,7 @@ BluetoothServiceBluedroid::BackendErrorNotification(bool aCrashed) * Reset following profile manager states for unexpected backend crash. * - HFP: connection state and audio state * - A2DP: connection state + * - HID: connection state */ BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); NS_ENSURE_TRUE_VOID(hfp); @@ -2639,6 +2640,9 @@ BluetoothServiceBluedroid::BackendErrorNotification(bool aCrashed) BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get(); NS_ENSURE_TRUE_VOID(a2dp); a2dp->HandleBackendError(); + BluetoothHidManager* hid = BluetoothHidManager::Get(); + NS_ENSURE_TRUE_VOID(hid); + hid->HandleBackendError(); mIsRestart = true; BT_LOGR("Recovery step2: stop bluetooth"); diff --git a/dom/bluetooth/common/BluetoothHidManager.cpp b/dom/bluetooth/bluez/BluetoothHidManager.cpp similarity index 100% rename from dom/bluetooth/common/BluetoothHidManager.cpp rename to dom/bluetooth/bluez/BluetoothHidManager.cpp diff --git a/dom/bluetooth/common/BluetoothHidManager.h b/dom/bluetooth/bluez/BluetoothHidManager.h similarity index 100% rename from dom/bluetooth/common/BluetoothHidManager.h rename to dom/bluetooth/bluez/BluetoothHidManager.h diff --git a/dom/bluetooth/common/BluetoothCommon.h b/dom/bluetooth/common/BluetoothCommon.h index 88d495b8f331..3c1572533501 100644 --- a/dom/bluetooth/common/BluetoothCommon.h +++ b/dom/bluetooth/common/BluetoothCommon.h @@ -207,6 +207,7 @@ extern bool gBluetoothDebugFlag; */ #define A2DP_STATUS_CHANGED_ID "a2dpstatuschanged" #define HFP_STATUS_CHANGED_ID "hfpstatuschanged" +#define HID_STATUS_CHANGED_ID "hidstatuschanged" #define SCO_STATUS_CHANGED_ID "scostatuschanged" /** @@ -307,6 +308,13 @@ extern bool gBluetoothDebugFlag; */ #define BLUETOOTH_GATT_MAX_ATTR_LEN 600 +/** + * The maximum descriptor length defined in BlueZ ipc spec. + * Please refer to http://git.kernel.org/cgit/bluetooth/bluez.git/tree/\ + * android/hal-ipc-api.txt#n532 + */ +#define BLUETOOTH_HID_MAX_DESC_LEN 884 + BEGIN_BLUETOOTH_NAMESPACE enum BluetoothStatus { @@ -876,6 +884,64 @@ enum BluetoothSocketType { EL2CAP = 4 }; +struct BluetoothHidInfoParam { + uint16_t mAttributeMask; + uint8_t mSubclass; + uint8_t mApplicationId; + uint16_t mVendorId; + uint16_t mProductId; + uint16_t mVersion; + uint8_t mCountryCode; + uint16_t mDescriptorLength; + uint8_t mDescriptorValue[BLUETOOTH_HID_MAX_DESC_LEN]; +}; + +struct BluetoothHidReport { + nsTArray mReportData; +}; + +enum BluetoothHidProtocolMode { + HID_PROTOCOL_MODE_REPORT = 0x00, + HID_PROTOCOL_MODE_BOOT = 0x01, + HID_PROTOCOL_MODE_UNSUPPORTED = 0xff +}; + +enum BluetoothHidReportType { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03 +}; + +enum BluetoothHidConnectionState { + HID_CONNECTION_STATE_CONNECTED, + HID_CONNECTION_STATE_CONNECTING, + HID_CONNECTION_STATE_DISCONNECTED, + HID_CONNECTION_STATE_DISCONNECTING, + HID_CONNECTION_STATE_FAILED_MOUSE_FROM_HOST, + HID_CONNECTION_STATE_FAILED_KEYBOARD_FROM_HOST, + HID_CONNECTION_STATE_FAILED_TOO_MANY_DEVICES, + HID_CONNECTION_STATE_FAILED_NO_HID_DRIVER, + HID_CONNECTION_STATE_FAILED_GENERIC, + HID_CONNECTION_STATE_UNKNOWN +}; + +enum BluetoothHidStatus { + HID_STATUS_OK, + HID_STATUS_HANDSHAKE_DEVICE_NOT_READY, + HID_STATUS_HANDSHAKE_INVALID_REPORT_ID, + HID_STATUS_HANDSHAKE_TRANSACTION_NOT_SPT, + HID_STATUS_HANDSHAKE_INVALID_PARAMETER, + HID_STATUS_HANDSHAKE_GENERIC_ERROR, + HID_STATUS_GENERAL_ERROR, + HID_STATUS_SDP_ERROR, + HID_STATUS_SET_PROTOCOL_ERROR, + HID_STATUS_DEVICE_DATABASE_FULL, + HID_STATUS_DEVICE_TYPE_NOT_SUPPORTED, + HID_STATUS_NO_RESOURCES, + HID_STATUS_AUTHENTICATION_FAILED, + HID_STATUS_HDL +}; + enum BluetoothHandsfreeAtResponse { HFP_AT_RESPONSE_ERROR, HFP_AT_RESPONSE_OK diff --git a/dom/bluetooth/common/BluetoothInterface.cpp b/dom/bluetooth/common/BluetoothInterface.cpp index c704691c56ec..35c3c65d980a 100644 --- a/dom/bluetooth/common/BluetoothInterface.cpp +++ b/dom/bluetooth/common/BluetoothInterface.cpp @@ -267,6 +267,111 @@ BluetoothSocketResultHandler::Accept(int aSockFd, BluetoothSocketInterface::~BluetoothSocketInterface() { } +// +//Hid Interface +// + +//Notification handling +// + +BluetoothHidNotificationHandler::BluetoothHidNotificationHandler() +{ } + +BluetoothHidNotificationHandler::~BluetoothHidNotificationHandler() +{ } + +void +BluetoothHidNotificationHandler::ConnectionStateNotification( + const BluetoothAddress& aBdAddr, BluetoothHidConnectionState aState) +{ } + +void +BluetoothHidNotificationHandler::HidInfoNotification( + const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam) +{ } + +void +BluetoothHidNotificationHandler::ProtocolModeNotification( + const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus, + BluetoothHidProtocolMode aProtocolMode) +{ } + +void +BluetoothHidNotificationHandler::IdleTimeNotification( + const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus, uint16_t aIdleTime) +{ } + +void +BluetoothHidNotificationHandler::GetReportNotification( + const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus, + const BluetoothHidReport& aReport) +{ } + +void +BluetoothHidNotificationHandler::VirtualUnplugNotification( + const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus) +{ } + +void +BluetoothHidNotificationHandler::HandshakeNotification( + const BluetoothAddress& aBdAddr, BluetoothHidStatus aStatus) +{ } + +// Result handling +// + +void BluetoothHidResultHandler::OnError(BluetoothStatus aStatus) +{ + BT_WARNING("Received error code %d", (int)aStatus); +} + +void +BluetoothHidResultHandler::Connect() +{ } + +void +BluetoothHidResultHandler::Disconnect() +{ } + +void +BluetoothHidResultHandler::VirtualUnplug() +{ } + +void +BluetoothHidResultHandler::SetInfo() +{ } + +void +BluetoothHidResultHandler::GetProtocol() +{ } + +void +BluetoothHidResultHandler::SetProtocol() +{ } + +void +BluetoothHidResultHandler::GetReport() +{ } + +void +BluetoothHidResultHandler::SetReport() +{ } + +void +BluetoothHidResultHandler::SendData() +{ } + +// Interface +// + +BluetoothHidInterface::BluetoothHidInterface() +{ } + +BluetoothHidInterface::~BluetoothHidInterface() +{ } + // // Handsfree Interface // diff --git a/dom/bluetooth/common/BluetoothInterface.h b/dom/bluetooth/common/BluetoothInterface.h index f8ca616c1838..f65cffae855a 100644 --- a/dom/bluetooth/common/BluetoothInterface.h +++ b/dom/bluetooth/common/BluetoothInterface.h @@ -276,6 +276,130 @@ protected: virtual ~BluetoothSocketInterface(); }; +// +// HID Interface +// + +class BluetoothHidNotificationHandler +{ +public: + virtual void + ConnectionStateNotification(const BluetoothAddress& aBdAddr, + BluetoothHidConnectionState aState); + + virtual void + HidInfoNotification( + const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam); + + virtual void + ProtocolModeNotification(const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus, + BluetoothHidProtocolMode aProtocolMode); + + virtual void + IdleTimeNotification(const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus, + uint16_t aIdleTime); + + virtual void + GetReportNotification(const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus, + const BluetoothHidReport& aReport); + + virtual void + VirtualUnplugNotification(const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus); + + virtual void + HandshakeNotification(const BluetoothAddress& aBdAddr, + BluetoothHidStatus aStatus); + +protected: + BluetoothHidNotificationHandler(); + virtual ~BluetoothHidNotificationHandler(); +}; + +class BluetoothHidResultHandler + : public mozilla::ipc::DaemonSocketResultHandler +{ +public: + virtual void OnError(BluetoothStatus aStatus); + + virtual void Connect(); + virtual void Disconnect(); + + virtual void VirtualUnplug(); + + virtual void SetInfo(); + + virtual void GetProtocol(); + virtual void SetProtocol(); + + virtual void GetReport(); + virtual void SetReport(); + + virtual void SendData(); + +protected: + virtual ~BluetoothHidResultHandler() { } +}; + +class BluetoothHidInterface +{ +public: + virtual void SetNotificationHandler( + BluetoothHidNotificationHandler* aNotificationHandler) = 0; + + /* Connect / Disconnect */ + + virtual void Connect(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) = 0; + virtual void Disconnect(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) = 0; + + /* Virtual Unplug */ + virtual void VirtualUnplug(const BluetoothAddress& aBdAddr, + BluetoothHidResultHandler* aRes) = 0; + + /* Set Info */ + + virtual void SetInfo(const BluetoothAddress& aBdAddr, + const BluetoothHidInfoParam& aHidInfoParam, + BluetoothHidResultHandler* aRes) = 0; + + /* Protocol */ + + virtual void GetProtocol(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtoclMode, + BluetoothHidResultHandler* aRes) = 0; + virtual void SetProtocol(const BluetoothAddress& aBdAddr, + BluetoothHidProtocolMode aHidProtocolMode, + BluetoothHidResultHandler* aRes) = 0; + + /* Report */ + + virtual void GetReport(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + uint8_t aReportId, + uint16_t aBuffSize, + BluetoothHidResultHandler* aRes) = 0; + virtual void SetReport(const BluetoothAddress& aBdAddr, + BluetoothHidReportType aType, + const BluetoothHidReport& aReport, + BluetoothHidResultHandler* aRes) = 0; + + /* Send Data */ + + virtual void SendData(const BluetoothAddress& aBdAddr, + uint16_t aDataLen, const uint8_t* aData, + BluetoothHidResultHandler* aRes) = 0; + +protected: + BluetoothHidInterface(); + virtual ~BluetoothHidInterface(); +}; + // // Handsfree Interface // @@ -1154,6 +1278,7 @@ public: virtual BluetoothA2dpInterface* GetBluetoothA2dpInterface() = 0; virtual BluetoothAvrcpInterface* GetBluetoothAvrcpInterface() = 0; virtual BluetoothGattInterface* GetBluetoothGattInterface() = 0; + virtual BluetoothHidInterface* GetBluetoothHidInterface() = 0; protected: BluetoothInterface(); diff --git a/dom/bluetooth/common/BluetoothProfileController.cpp b/dom/bluetooth/common/BluetoothProfileController.cpp index 2a5664ce6764..ec711f4d4429 100644 --- a/dom/bluetooth/common/BluetoothProfileController.cpp +++ b/dom/bluetooth/common/BluetoothProfileController.cpp @@ -188,6 +188,7 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass) AddProfile(BluetoothHfpManager::Get()); AddProfile(BluetoothA2dpManager::Get()); AddProfile(BluetoothAvrcpManager::Get()); // register after A2DP + AddProfile(BluetoothHidManager::Get()); return; } diff --git a/dom/bluetooth/common/webapi/BluetoothAdapter.cpp b/dom/bluetooth/common/webapi/BluetoothAdapter.cpp index 6673124c97ef..05b5b2a61ead 100644 --- a/dom/bluetooth/common/webapi/BluetoothAdapter.cpp +++ b/dom/bluetooth/common/webapi/BluetoothAdapter.cpp @@ -539,6 +539,7 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData) HandleDeviceUnpaired(aData.value()); } else if (aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) || aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) || + aData.name().EqualsLiteral(HID_STATUS_CHANGED_ID) || aData.name().EqualsLiteral(A2DP_STATUS_CHANGED_ID)) { MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); const InfallibleTArray& arr = diff --git a/dom/bluetooth/common/webapi/BluetoothAdapter.h b/dom/bluetooth/common/webapi/BluetoothAdapter.h index 0272005c2574..129668e6a21b 100644 --- a/dom/bluetooth/common/webapi/BluetoothAdapter.h +++ b/dom/bluetooth/common/webapi/BluetoothAdapter.h @@ -90,9 +90,10 @@ public: IMPL_EVENT_HANDLER(devicepaired); IMPL_EVENT_HANDLER(deviceunpaired); IMPL_EVENT_HANDLER(pairingaborted); - // HFP/A2DP/AVRCP + // HFP/A2DP/AVRCP/HID IMPL_EVENT_HANDLER(a2dpstatuschanged); IMPL_EVENT_HANDLER(hfpstatuschanged); + IMPL_EVENT_HANDLER(hidstatuschanged); IMPL_EVENT_HANDLER(scostatuschanged); IMPL_EVENT_HANDLER(requestmediaplaystatus); // PBAP diff --git a/dom/bluetooth/moz.build b/dom/bluetooth/moz.build index a4ec33667c4e..aa401ac75e0a 100644 --- a/dom/bluetooth/moz.build +++ b/dom/bluetooth/moz.build @@ -17,7 +17,6 @@ if CONFIG['MOZ_B2G_BT']: SOURCES += [ 'common/BluetoothGattReplyRunnable.cpp', - 'common/BluetoothHidManager.cpp', 'common/BluetoothInterface.cpp', 'common/BluetoothProfileController.cpp', 'common/BluetoothReplyRunnable.cpp', @@ -65,6 +64,7 @@ if CONFIG['MOZ_B2G_BT']: 'bluez/BluetoothAvrcpManager.cpp', 'bluez/BluetoothDBusService.cpp', 'bluez/BluetoothHfpManager.cpp', + 'bluez/BluetoothHidManager.cpp', 'bluez/BluetoothOppManager.cpp', 'bluez/BluetoothSocket.cpp', 'bluez/BluetoothUnixSocketConnector.cpp' @@ -83,10 +83,12 @@ if CONFIG['MOZ_B2G_BT']: 'bluedroid/BluetoothDaemonGattInterface.cpp', 'bluedroid/BluetoothDaemonHandsfreeInterface.cpp', 'bluedroid/BluetoothDaemonHelpers.cpp', + 'bluedroid/BluetoothDaemonHidInterface.cpp', 'bluedroid/BluetoothDaemonInterface.cpp', 'bluedroid/BluetoothDaemonSetupInterface.cpp', 'bluedroid/BluetoothDaemonSocketInterface.cpp', 'bluedroid/BluetoothGattManager.cpp', + 'bluedroid/BluetoothHidManager.cpp', 'bluedroid/BluetoothMapBMessage.cpp', 'bluedroid/BluetoothMapFolder.cpp', 'bluedroid/BluetoothMapSmsManager.cpp', diff --git a/dom/webidl/BluetoothAdapter.webidl b/dom/webidl/BluetoothAdapter.webidl index 6b9555a3e600..c0d0d3b41785 100644 --- a/dom/webidl/BluetoothAdapter.webidl +++ b/dom/webidl/BluetoothAdapter.webidl @@ -63,6 +63,9 @@ interface BluetoothAdapter : EventTarget { // Fired when handsfree connection status changed attribute EventHandler onhfpstatuschanged; + // Fired when handsfree connection status changed + attribute EventHandler onhidstatuschanged; + // Fired when sco connection status changed attribute EventHandler onscostatuschanged; From c9c042d9344ea2f08da5f799578e2f635e16582e Mon Sep 17 00:00:00 2001 From: Bogdan Postelnicu Date: Mon, 14 Mar 2016 10:36:36 +0200 Subject: [PATCH 12/24] Bug 1230907 - mPixelRowSize added to constructor with 0 value. r=seth --- image/decoders/nsBMPDecoder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/image/decoders/nsBMPDecoder.cpp b/image/decoders/nsBMPDecoder.cpp index b67e255231bc..c7dce103fb4c 100644 --- a/image/decoders/nsBMPDecoder.cpp +++ b/image/decoders/nsBMPDecoder.cpp @@ -177,6 +177,7 @@ nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength) , mColors(nullptr) , mBytesPerColor(0) , mPreGapLength(0) + , mPixelRowSize(0) , mCurrentRow(0) , mCurrentPos(0) , mAbsoluteModeNumPixels(0) From 69b0584aebe6026b6d5a0098725d8a4a9e8f0522 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 10 Mar 2016 21:00:07 +0100 Subject: [PATCH 13/24] Bug 1255772: Implement wasm::Unreachable; r=sunfish MozReview-Commit-ID: BFDrX72K7Vk --HG-- extra : rebase_source : 6251f5e005dd80e4f93c49d2d92a4fb34d073913 extra : amend_source : 0939036518ce2eb9a8ea4f5ea8439c86e7e34a79 --- js/src/asmjs/Wasm.cpp | 9 ++++++++ js/src/asmjs/WasmBinaryToText.cpp | 8 +++++++ js/src/asmjs/WasmIonCompile.cpp | 22 +++++++++++++++++-- js/src/asmjs/WasmStubs.cpp | 5 ++++- js/src/asmjs/WasmTextToBinary.cpp | 16 ++++++++++++++ js/src/asmjs/WasmTypes.cpp | 9 ++++++++ js/src/asmjs/WasmTypes.h | 2 ++ .../jit-test/tests/wasm/basic-control-flow.js | 10 +++++++++ js/src/jit/CodeGenerator.cpp | 7 ++++++ js/src/jit/CodeGenerator.h | 1 + js/src/jit/Lowering.cpp | 6 +++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 16 ++++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/shared/LIR-shared.h | 9 ++++++++ js/src/jit/shared/LOpcodes-shared.h | 1 + js/src/js.msg | 1 + js/src/vm/Xdr.h | 4 ++-- 18 files changed, 123 insertions(+), 5 deletions(-) diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index c332630e4826..b740e123a5d5 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -184,6 +184,13 @@ DecodeNop(FunctionDecoder& f, ExprType* type) return true; } +static bool +DecodeUnreachable(FunctionDecoder& f, ExprType* type) +{ + *type = AnyType; + return true; +} + static bool DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType* type) { @@ -843,6 +850,8 @@ DecodeExpr(FunctionDecoder& f, ExprType* type) return DecodeBrTable(f, type); case Expr::Return: return DecodeReturn(f, type); + case Expr::Unreachable: + return DecodeUnreachable(f, type); default: // Note: it's important not to remove this default since readExpr() // can return Expr values for which there is no enumerator. diff --git a/js/src/asmjs/WasmBinaryToText.cpp b/js/src/asmjs/WasmBinaryToText.cpp index bf8bc3af72b2..680275852276 100644 --- a/js/src/asmjs/WasmBinaryToText.cpp +++ b/js/src/asmjs/WasmBinaryToText.cpp @@ -158,6 +158,12 @@ RenderNop(WasmRenderContext& c) return c.buffer.append("(nop)"); } +static bool +RenderUnreachable(WasmRenderContext& c) +{ + return c.buffer.append("(trap)"); +} + static bool RenderCallWithSig(WasmRenderContext& c, uint32_t sigIndex) { @@ -877,6 +883,8 @@ RenderExpr(WasmRenderContext& c) switch (expr) { case Expr::Nop: return RenderNop(c); + case Expr::Unreachable: + return RenderUnreachable(c); case Expr::Call: return RenderCall(c); case Expr::CallImport: diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index c8c8c6edc2bf..375b16fc7089 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -865,6 +865,17 @@ class FunctionCompiler curBlock_ = nullptr; } + bool unreachableTrap() + { + if (inDeadCode()) + return true; + + auto* ins = MAsmThrowUnreachable::New(alloc()); + curBlock_->end(ins); + curBlock_ = nullptr; + return true; + } + bool branchAndStartThen(MDefinition* cond, MBasicBlock** thenBlock, MBasicBlock** elseBlock) { if (inDeadCode()) @@ -2617,6 +2628,13 @@ EmitReturn(FunctionCompiler& f, MDefinition** def) return true; } +static bool +EmitUnreachable(FunctionCompiler& f, MDefinition** def) +{ + *def = nullptr; + return f.unreachableTrap(); +} + static bool EmitBlock(FunctionCompiler& f, MDefinition** def) { @@ -2690,6 +2708,8 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) return EmitBrTable(f, def); case Expr::Return: return EmitReturn(f, def); + case Expr::Unreachable: + return EmitUnreachable(f, def); // Calls case Expr::Call: @@ -2699,7 +2719,6 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::CallImport: return EmitCallImport(f, exprOffset, def); - // Locals and globals case Expr::GetLocal: return EmitGetLocal(f, def); @@ -3033,7 +3052,6 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) case Expr::I64Rotl: case Expr::MemorySize: case Expr::GrowMemory: - case Expr::Unreachable: MOZ_CRASH("NYI"); break; case Expr::Limit:; diff --git a/js/src/asmjs/WasmStubs.cpp b/js/src/asmjs/WasmStubs.cpp index 4ed92448cf11..96917a66ca3f 100644 --- a/js/src/asmjs/WasmStubs.cpp +++ b/js/src/asmjs/WasmStubs.cpp @@ -776,7 +776,8 @@ GenerateErrorStub(MacroAssembler& masm, SymbolicAddress address) offsets.begin = masm.currentOffset(); // sp can be anything at this point, so ensure it is aligned when calling - // into C++. We unconditionally jump to throw so don't worry about restoring sp. + // into C++. We unconditionally jump to throw so don't worry about + // restoring sp. masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1))); masm.assertStackAlignment(ABIStackAlignment); @@ -832,6 +833,8 @@ wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target) return GenerateErrorStub(masm, SymbolicAddress::OnOutOfBounds); case JumpTarget::BadIndirectCall: return GenerateErrorStub(masm, SymbolicAddress::BadIndirectCall); + case JumpTarget::UnreachableTrap: + return GenerateErrorStub(masm, SymbolicAddress::UnreachableTrap); case JumpTarget::Throw: return GenerateThrow(masm); case JumpTarget::Limit: diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index da72392e2c45..6bd86ffecfbc 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -209,6 +209,7 @@ enum class WasmAstExprKind Return, SetLocal, Store, + Trap, UnaryOperator, }; @@ -238,6 +239,13 @@ struct WasmAstNop : WasmAstExpr {} }; +struct WasmAstTrap : WasmAstExpr +{ + WasmAstTrap() + : WasmAstExpr(WasmAstExprKind::Trap) + {} +}; + class WasmAstConst : public WasmAstExpr { const Val val_; @@ -796,6 +804,7 @@ class WasmToken Store, Table, Text, + Trap, Type, UnaryOpcode, ValueType @@ -1931,6 +1940,8 @@ WasmTokenStream::next() return WasmToken(WasmToken::Table, begin, cur_); if (consume(MOZ_UTF16("type"))) return WasmToken(WasmToken::Type, begin, cur_); + if (consume(MOZ_UTF16("trap"))) + return WasmToken(WasmToken::Trap, begin, cur_); break; default: @@ -2669,6 +2680,8 @@ ParseExprInsideParens(WasmParseContext& c) switch (token.kind()) { case WasmToken::Nop: return new(c.lifo) WasmAstNop; + case WasmToken::Trap: + return new(c.lifo) WasmAstTrap; case WasmToken::BinaryOpcode: return ParseBinaryOperator(c, token.expr()); case WasmToken::Block: @@ -3373,6 +3386,7 @@ ResolveExpr(Resolver& r, WasmAstExpr& expr) { switch (expr.kind()) { case WasmAstExprKind::Nop: + case WasmAstExprKind::Trap: return true; case WasmAstExprKind::BinaryOperator: return ResolveBinaryOperator(r, expr.as()); @@ -3703,6 +3717,8 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr) switch (expr.kind()) { case WasmAstExprKind::Nop: return e.writeExpr(Expr::Nop); + case WasmAstExprKind::Trap: + return e.writeExpr(Expr::Unreachable); case WasmAstExprKind::BinaryOperator: return EncodeBinaryOperator(e, expr.as()); case WasmAstExprKind::Block: diff --git a/js/src/asmjs/WasmTypes.cpp b/js/src/asmjs/WasmTypes.cpp index 63043d3cc9de..0a8f60949039 100644 --- a/js/src/asmjs/WasmTypes.cpp +++ b/js/src/asmjs/WasmTypes.cpp @@ -79,6 +79,13 @@ BadIndirectCall() JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IND_CALL); } +static void +UnreachableTrap() +{ + JSContext* cx = JSRuntime::innermostWasmActivation()->cx(); + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNREACHABLE); +} + static int32_t CoerceInPlace_ToInt32(MutableHandleValue val) { @@ -186,6 +193,8 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx) return FuncCast(OnImpreciseConversion, Args_General0); case SymbolicAddress::BadIndirectCall: return FuncCast(BadIndirectCall, Args_General0); + case SymbolicAddress::UnreachableTrap: + return FuncCast(UnreachableTrap, Args_General0); case SymbolicAddress::HandleExecutionInterrupt: return FuncCast(WasmHandleExecutionInterrupt, Args_General0); case SymbolicAddress::InvokeImport_Void: diff --git a/js/src/asmjs/WasmTypes.h b/js/src/asmjs/WasmTypes.h index 82fd6a8f0bcb..54408fab8367 100644 --- a/js/src/asmjs/WasmTypes.h +++ b/js/src/asmjs/WasmTypes.h @@ -535,6 +535,7 @@ enum class SymbolicAddress OnOutOfBounds, OnImpreciseConversion, BadIndirectCall, + UnreachableTrap, HandleExecutionInterrupt, InvokeImport_Void, InvokeImport_I32, @@ -558,6 +559,7 @@ enum class JumpTarget OutOfBounds, ConversionError, BadIndirectCall, + UnreachableTrap, Throw, Limit }; diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js index bdd9085d8c71..3d2c137a5346 100644 --- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -406,3 +406,13 @@ assertEq(f(1), 0); assertEq(f(2), 2); assertEq(f(3), -1); +// ---------------------------------------------------------------------------- +// unreachable + +const UNREACHABLE = /unreachable/; +assertErrorMessage(wasmEvalText(`(module (func (trap)) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (if (trap) (i32.const 0))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (trap)))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (trap)))) (export "" 0))`), Error, UNREACHABLE); +assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (trap))) (export "" 0))`), Error, UNREACHABLE); + diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 804a66355a8d..8625bba3b914 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -10627,6 +10627,13 @@ CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir) masm.bind(&rejoin); } +void +CodeGenerator::visitAsmThrowUnreachable(LAsmThrowUnreachable* lir) +{ + MOZ_ASSERT(gen->compilingAsmJS()); + masm.jump(wasm::JumpTarget::UnreachableTrap); +} + typedef bool (*RecompileFn)(JSContext*); static const VMFunction RecompileFnInfo = FunctionInfo(Recompile); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 19d6d676793e..8e2b33f4c23b 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -395,6 +395,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitInterruptCheck(LInterruptCheck* lir); void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins); void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir); + void visitAsmThrowUnreachable(LAsmThrowUnreachable* lir); void visitRecompileCheck(LRecompileCheck* ins); void visitRandom(LRandom* ins); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index adf73902db52..1c7978d0880d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2466,6 +2466,12 @@ LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins) add(new(alloc()) LAsmJSInterruptCheck, ins); } +void +LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins) +{ + add(new(alloc()) LAsmThrowUnreachable, ins); +} + void LIRGenerator::visitStoreSlot(MStoreSlot* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 662957d8c90a..3f8d34ac31e6 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -183,6 +183,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitFunctionEnvironment(MFunctionEnvironment* ins); void visitInterruptCheck(MInterruptCheck* ins); void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins); + void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins); void visitStoreSlot(MStoreSlot* ins); void visitFilterTypeSet(MFilterTypeSet* ins); void visitTypeBarrier(MTypeBarrier* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 02e7a09b63db..a703febc7a48 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -7592,6 +7592,22 @@ class MAsmJSInterruptCheck } }; +// Directly jumps to the unreachable trap handler. +class MAsmThrowUnreachable + : public MAryControlInstruction<0, 0>, + public NoTypePolicy::Data +{ + public: + INSTRUCTION_HEADER(AsmThrowUnreachable) + static MAsmThrowUnreachable* New(TempAllocator& alloc) { + return new(alloc) MAsmThrowUnreachable; + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving // it to baseline to throw at the correct pc. class MLexicalCheck diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index bfa3a0f358e5..f67d8d08ec8f 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -253,6 +253,7 @@ namespace jit { _(CallInstanceOf) \ _(InterruptCheck) \ _(AsmJSInterruptCheck) \ + _(AsmThrowUnreachable) \ _(GetDOMProperty) \ _(GetDOMMember) \ _(SetDOMProperty) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index cb970ddcdb6f..0c1dc1c278e3 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1247,6 +1247,15 @@ class LAsmJSInterruptCheck : public LInstructionHelper<0, 0, 0> } }; +class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0> +{ + public: + LIR_HEADER(AsmThrowUnreachable); + + LAsmThrowUnreachable() + { } +}; + class LInterruptCheck : public LInstructionHelper<0, 0, 0> { Label* oolEntry_; diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 20a866e064e7..a2b2cef3c929 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -344,6 +344,7 @@ _(CallInstanceOf) \ _(InterruptCheck) \ _(AsmJSInterruptCheck) \ + _(AsmThrowUnreachable) \ _(GetDOMProperty) \ _(GetDOMMemberV) \ _(GetDOMMemberT) \ diff --git a/js/src/js.msg b/js/src/js.msg index 0f0090c3a90a..7617dd068aab 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -349,6 +349,7 @@ MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0} MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "wasm indirect call signature mismatch") MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be a typed array") MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object") +MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_ERR, "reached unreachable trap") // Proxy MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 5e9fbc7dd295..3cf17c222358 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -31,11 +31,11 @@ namespace js { * * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".) */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 351; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 352; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 418, +static_assert(JSErr_Limit == 419, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " From 47124ee67245296412ac0028b8e10c3e69b0b6ba Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Mon, 14 Mar 2016 11:36:39 +0100 Subject: [PATCH 14/24] Backed out 4 changesets (bug 1222624) to fix bug 1249572 Backed out changeset e407f668476d (bug 1222624) Backed out changeset 2a4502574495 (bug 1222624) Backed out changeset 4b6ae937a03d (bug 1222624) Backed out changeset cfa2b6acc2ba (bug 1222624) --- dom/xslt/base/txURIUtils.cpp | 24 +++ dom/xslt/base/txURIUtils.h | 8 + dom/xslt/nsIXSLTProcessorPrivate.idl | 9 +- dom/xslt/tests/mochitest/file_bug1222624.xml | 4 - dom/xslt/tests/mochitest/file_bug1222624.xsl | 12 -- .../tests/mochitest/file_bug1222624_data1.xml | 1 - .../tests/mochitest/file_bug1222624_data2.xml | 1 - .../tests/mochitest/file_bug1222624_sub.xsl | 4 - .../mochitest/file_bug1222624_sub_sub.xsl | 6 - dom/xslt/tests/mochitest/mochitest.ini | 2 - dom/xslt/tests/mochitest/test_bug1222624.html | 53 ----- dom/xslt/xml/txXMLParser.cpp | 36 ++-- dom/xslt/xml/txXMLParser.h | 8 +- dom/xslt/xpath/txMozillaXPathTreeWalker.cpp | 4 +- dom/xslt/xpath/txXPathTreeWalker.h | 2 +- dom/xslt/xslt/txDocumentFunctionCall.cpp | 54 +++-- dom/xslt/xslt/txExecutionState.cpp | 67 +++---- dom/xslt/xslt/txExecutionState.h | 23 ++- dom/xslt/xslt/txMozillaStylesheetCompiler.cpp | 178 ++++++++++------- dom/xslt/xslt/txMozillaXSLTProcessor.cpp | 41 +--- dom/xslt/xslt/txMozillaXSLTProcessor.h | 6 +- dom/xslt/xslt/txStylesheetCompileHandlers.cpp | 21 +- dom/xslt/xslt/txStylesheetCompiler.cpp | 189 +++++++----------- dom/xslt/xslt/txStylesheetCompiler.h | 34 ++-- dom/xslt/xslt/txXSLTFunctions.h | 6 +- 25 files changed, 358 insertions(+), 435 deletions(-) delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624.xml delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624.xsl delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624_data1.xml delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624_data2.xml delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624_sub.xsl delete mode 100644 dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl delete mode 100644 dom/xslt/tests/mochitest/test_bug1222624.html diff --git a/dom/xslt/base/txURIUtils.cpp b/dom/xslt/base/txURIUtils.cpp index 201f5d395477..33f7e919aa06 100644 --- a/dom/xslt/base/txURIUtils.cpp +++ b/dom/xslt/base/txURIUtils.cpp @@ -17,6 +17,30 @@ using mozilla::LoadInfo; * A set of utilities for handling URIs **/ +/** + * Resolves the given href argument, using the given documentBase + * if necessary. + * The new resolved href will be appended to the given dest String +**/ +void URIUtils::resolveHref(const nsAString& href, const nsAString& base, + nsAString& dest) { + if (base.IsEmpty()) { + dest.Append(href); + return; + } + if (href.IsEmpty()) { + dest.Append(base); + return; + } + nsCOMPtr pURL; + nsAutoString resultHref; + nsresult result = NS_NewURI(getter_AddRefs(pURL), base); + if (NS_SUCCEEDED(result)) { + NS_MakeAbsoluteURI(resultHref, href, pURL); + dest.Append(resultHref); + } +} //-- resolveHref + // static void URIUtils::ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode) diff --git a/dom/xslt/base/txURIUtils.h b/dom/xslt/base/txURIUtils.h index 188f7224df58..ca38538a8b82 100644 --- a/dom/xslt/base/txURIUtils.h +++ b/dom/xslt/base/txURIUtils.h @@ -23,6 +23,14 @@ public: * Reset the given document with the document of the source node */ static void ResetWithSource(nsIDocument *aNewDoc, nsIDOMNode *aSourceNode); + + /** + * Resolves the given href argument, using the given documentBase + * if necessary. + * The new resolved href will be appended to the given dest String + **/ + static void resolveHref(const nsAString& href, const nsAString& base, + nsAString& dest); }; //-- URIUtils /* */ diff --git a/dom/xslt/nsIXSLTProcessorPrivate.idl b/dom/xslt/nsIXSLTProcessorPrivate.idl index 9212f70fd392..d88320cef25c 100644 --- a/dom/xslt/nsIXSLTProcessorPrivate.idl +++ b/dom/xslt/nsIXSLTProcessorPrivate.idl @@ -5,16 +5,9 @@ #include "nsISupports.idl" -[scriptable, uuid(75d14f5d-293d-4872-8a26-e79268de592f)] +[scriptable, uuid(b8d727f7-67f4-4dc1-a318-ec0c87280816)] interface nsIXSLTProcessorPrivate : nsISupports { - /** - * This needs to be called if the XSLTProcessor is instantiated - * through the XPCOM registry (i.e. using do_createInstance) and the - * stylesheet uses xsl:import/xsl:include or the document() xpath function. - */ - void init(in nsISupports global); - /** * Disables all loading of external documents, such as from * and document() diff --git a/dom/xslt/tests/mochitest/file_bug1222624.xml b/dom/xslt/tests/mochitest/file_bug1222624.xml deleted file mode 100644 index c8290a33808d..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - file_bug1222624_data2.xml - diff --git a/dom/xslt/tests/mochitest/file_bug1222624.xsl b/dom/xslt/tests/mochitest/file_bug1222624.xsl deleted file mode 100644 index cf954c4fc81b..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624.xsl +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - ! - - - - diff --git a/dom/xslt/tests/mochitest/file_bug1222624_data1.xml b/dom/xslt/tests/mochitest/file_bug1222624_data1.xml deleted file mode 100644 index f50fdbc1cbfb..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624_data1.xml +++ /dev/null @@ -1 +0,0 @@ -doc1 diff --git a/dom/xslt/tests/mochitest/file_bug1222624_data2.xml b/dom/xslt/tests/mochitest/file_bug1222624_data2.xml deleted file mode 100644 index e6228590c19c..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624_data2.xml +++ /dev/null @@ -1 +0,0 @@ -doc2 diff --git a/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl b/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl deleted file mode 100644 index 189031a1f364..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624_sub.xsl +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl b/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl deleted file mode 100644 index 881e4c55bcd3..000000000000 --- a/dom/xslt/tests/mochitest/file_bug1222624_sub_sub.xsl +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/dom/xslt/tests/mochitest/mochitest.ini b/dom/xslt/tests/mochitest/mochitest.ini index 08ae18980889..53a6d001cd6b 100644 --- a/dom/xslt/tests/mochitest/mochitest.ini +++ b/dom/xslt/tests/mochitest/mochitest.ini @@ -16,7 +16,5 @@ [test_bug667315.html] [test_bug1135764.html] support-files = file_bug1135764.xml file_bug1135764.xsl -[test_bug1222624.html] -support-files = file_bug1222624.xml file_bug1222624.xsl file_bug1222624_sub.xsl file_bug1222624_sub_sub.xsl file_bug1222624_data1.xml file_bug1222624_data2.xml [test_exslt_regex.html] [test_parameter.html] diff --git a/dom/xslt/tests/mochitest/test_bug1222624.html b/dom/xslt/tests/mochitest/test_bug1222624.html deleted file mode 100644 index 46e47a6e0f44..000000000000 --- a/dom/xslt/tests/mochitest/test_bug1222624.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Test for Bug 1222624 - - - - -Mozilla Bug 1222624 -

- -
-
-
- - diff --git a/dom/xslt/xml/txXMLParser.cpp b/dom/xslt/xml/txXMLParser.cpp index 8882cdfbf8b7..0cf281f2cff5 100644 --- a/dom/xslt/xml/txXMLParser.cpp +++ b/dom/xslt/xml/txXMLParser.cpp @@ -15,14 +15,20 @@ #include "nsIPrincipal.h" nsresult -txParseDocumentFromURI(nsIURI* aUri, - nsIDocument* aLoadingDocument, +txParseDocumentFromURI(const nsAString& aHref, + const txXPathNode& aLoader, nsAString& aErrMsg, txXPathNode** aResult) { + NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; + nsCOMPtr documentURI; + nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref); + NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr loadGroup = aLoadingDocument->GetDocumentLoadGroup(); + nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader); + + nsCOMPtr loadGroup = loaderDocument->GetDocumentLoadGroup(); // For the system principal loaderUri will be null here, which is good // since that means that chrome documents can load any uri. @@ -30,24 +36,20 @@ txParseDocumentFromURI(nsIURI* aUri, // Raw pointer, we want the resulting txXPathNode to hold a reference to // the document. nsIDOMDocument* theDocument = nullptr; - nsAutoSyncOperation sync(aLoadingDocument); - nsresult rv = - nsSyncLoadService::LoadDocument(aUri, - nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, - aLoadingDocument->NodePrincipal(), - nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, - loadGroup, - true, - aLoadingDocument->GetReferrerPolicy(), - &theDocument); + nsAutoSyncOperation sync(loaderDocument); + rv = nsSyncLoadService::LoadDocument(documentURI, + nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST, + loaderDocument->NodePrincipal(), + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + loadGroup, true, + loaderDocument->GetReferrerPolicy(), + &theDocument); if (NS_FAILED(rv)) { aErrMsg.AppendLiteral("Document load of "); - nsAutoCString spec; - aUri->GetSpec(spec); - aErrMsg.Append(NS_ConvertUTF8toUTF16(spec)); + aErrMsg.Append(aHref); aErrMsg.AppendLiteral(" failed."); - return rv; + return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE; } *aResult = txXPathNativeNode::createXPathNode(theDocument); diff --git a/dom/xslt/xml/txXMLParser.h b/dom/xslt/xml/txXMLParser.h index 2153d90dc112..fea9defe34b2 100644 --- a/dom/xslt/xml/txXMLParser.h +++ b/dom/xslt/xml/txXMLParser.h @@ -9,8 +9,6 @@ #include "txCore.h" class txXPathNode; -class nsIURI; -class nsIDocument; /** * API to load XML files into DOM datastructures. @@ -22,9 +20,7 @@ class nsIDocument; * of the document aLoader. */ extern "C" nsresult -txParseDocumentFromURI(nsIURI* aUri, - nsIDocument* aLoadingDocument, - nsAString& aErrMsg, - txXPathNode** aResult); +txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader, + nsAString& aErrMsg, txXPathNode** aResult); #endif diff --git a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp index fee671ea59ff..35bc527d769e 100644 --- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp +++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp @@ -563,9 +563,9 @@ txXPathNodeUtils::getXSLTId(const txXPathNode& aNode, /* static */ void -txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsIURI** aUri) +txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI) { - *aUri = aNode.mNode->GetBaseURI().take(); + aNode.mNode->GetBaseURI(aURI); } /* static */ diff --git a/dom/xslt/xpath/txXPathTreeWalker.h b/dom/xslt/xpath/txXPathTreeWalker.h index 1c66f2c8ba70..26cb42ddd420 100644 --- a/dom/xslt/xpath/txXPathTreeWalker.h +++ b/dom/xslt/xpath/txXPathTreeWalker.h @@ -93,7 +93,7 @@ public: static nsresult getXSLTId(const txXPathNode& aNode, const txXPathNode& aBase, nsAString& aResult); static void release(txXPathNode* aNode); - static void getBaseURI(const txXPathNode& aNode, nsIURI** aURI); + static void getBaseURI(const txXPathNode& aNode, nsAString& aURI); static int comparePosition(const txXPathNode& aNode, const txXPathNode& aOtherNode); static bool localNameEquals(const txXPathNode& aNode, diff --git a/dom/xslt/xslt/txDocumentFunctionCall.cpp b/dom/xslt/xslt/txDocumentFunctionCall.cpp index 1de5cf75c8cc..3ea7a83b24d3 100644 --- a/dom/xslt/xslt/txDocumentFunctionCall.cpp +++ b/dom/xslt/xslt/txDocumentFunctionCall.cpp @@ -13,33 +13,46 @@ #include "txXSLTFunctions.h" #include "txExecutionState.h" #include "txURIUtils.h" -#include "nsIURI.h" -#include "nsNetUtil.h" + +/* + * Creates a new DocumentFunctionCall. + */ +DocumentFunctionCall::DocumentFunctionCall(const nsAString& aBaseURI) + : mBaseURI(aBaseURI) +{ +} static void -retrieveNode(txExecutionState* aExecutionState, - const nsAString& aUri, - nsIURI* aBaseUri, - txNodeSet* aNodeSet) +retrieveNode(txExecutionState* aExecutionState, const nsAString& aUri, + const nsAString& aBaseUri, txNodeSet* aNodeSet) { - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri, nullptr, aBaseUri); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; + nsAutoString absUrl; + URIUtils::resolveHref(aUri, aBaseUri, absUrl); + + int32_t hash = absUrl.RFindChar(char16_t('#')); + uint32_t urlEnd, fragStart, fragEnd; + if (hash == kNotFound) { + urlEnd = absUrl.Length(); + fragStart = 0; + fragEnd = 0; + } + else { + urlEnd = hash; + fragStart = hash + 1; + fragEnd = absUrl.Length(); } - nsAutoCString frag; - uri->GetRef(frag); - uri->SetRef(EmptyCString()); + nsDependentSubstring docUrl(absUrl, 0, urlEnd); + nsDependentSubstring frag(absUrl, fragStart, fragEnd); - const txXPathNode* loadNode = aExecutionState->retrieveDocument(uri); + const txXPathNode* loadNode = aExecutionState->retrieveDocument(docUrl); if (loadNode) { if (frag.IsEmpty()) { aNodeSet->add(*loadNode); } else { txXPathTreeWalker walker(*loadNode); - if (walker.moveToElementById(NS_ConvertUTF8toUTF16(frag))) { + if (walker.moveToElementById(frag)) { aNodeSet->add(walker.getCurrentPosition()); } } @@ -74,7 +87,7 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult1)); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr baseURI; + nsAutoString baseURI; bool baseURISet = false; if (mParams.Length() == 2) { @@ -91,8 +104,7 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, baseURISet = true; if (!nodeSet2->isEmpty()) { - txXPathNodeUtils::getBaseURI(nodeSet2->get(0), - getter_AddRefs(baseURI)); + txXPathNodeUtils::getBaseURI(nodeSet2->get(0), baseURI); } } @@ -109,7 +121,7 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, if (!baseURISet) { // if the second argument wasn't specified, use // the baseUri of node itself - txXPathNodeUtils::getBaseURI(node, getter_AddRefs(baseURI)); + txXPathNodeUtils::getBaseURI(node, baseURI); } retrieveNode(es, uriStr, baseURI, nodeSet); } @@ -122,8 +134,8 @@ DocumentFunctionCall::evaluate(txIEvalContext* aContext, // The first argument is not a NodeSet nsAutoString uriStr; exprResult1->stringValue(uriStr); - nsIURI* base = baseURISet ? baseURI.get() : mBaseURI.get(); - retrieveNode(es, uriStr, base, nodeSet); + const nsAString* base = baseURISet ? &baseURI : &mBaseURI; + retrieveNode(es, uriStr, *base, nodeSet); NS_ADDREF(*aResult = nodeSet); diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp index 9389f63e0432..7bd3bdc07b34 100644 --- a/dom/xslt/xslt/txExecutionState.cpp +++ b/dom/xslt/xslt/txExecutionState.cpp @@ -21,28 +21,27 @@ txLoadedDocumentsHash::init(txXPathNode* aSourceDocument) { mSourceDocument = aSourceDocument; - nsCOMPtr baseURI; - txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI)); + nsAutoString baseURI; + txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); - LookupOrAdd(baseURI)->mDocument = mSourceDocument; + PutEntry(baseURI)->mDocument = mSourceDocument; } txLoadedDocumentsHash::~txLoadedDocumentsHash() { if (mSourceDocument) { - nsCOMPtr baseURI; - txXPathNodeUtils::getBaseURI(*mSourceDocument, getter_AddRefs(baseURI)); + nsAutoString baseURI; + txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI); - txLoadedDocumentInfo* info = Get(baseURI); - if (info) { - delete info->mDocument.forget(); + txLoadedDocumentEntry* entry = GetEntry(baseURI); + if (entry) { + delete entry->mDocument.forget(); } } } txExecutionState::txExecutionState(txStylesheet* aStylesheet, - bool aDisableLoads, - nsIDocument* aLoadingDocument) + bool aDisableLoads) : mOutputHandler(nullptr), mResultHandler(nullptr), mStylesheet(aStylesheet), @@ -52,7 +51,6 @@ txExecutionState::txExecutionState(txStylesheet* aStylesheet, mEvalContext(nullptr), mInitialEvalContext(nullptr), mGlobalParams(nullptr), - mLoadingDocument(aLoadingDocument), mKeyHash(aStylesheet->getKeyMap()), mDisableLoads(aDisableLoads) { @@ -374,48 +372,41 @@ txExecutionState::getEvalContext() } const txXPathNode* -txExecutionState::retrieveDocument(nsIURI* aUri) +txExecutionState::retrieveDocument(const nsAString& aUri) { -#ifdef DEBUG - { - bool hasFrag; - aUri->GetHasRef(&hasFrag); - MOZ_ASSERT(!hasFrag, "Remove the fragment"); - } -#endif + NS_ASSERTION(!aUri.Contains(char16_t('#')), + "Remove the fragment."); - if (mDisableLoads || !mLoadingDocument) { + if (mDisableLoads) { return nullptr; } - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) { - nsAutoCString spec; - aUri->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Debug, - ("Retrieve Document %s", spec.get())); - } + MOZ_LOG(txLog::xslt, LogLevel::Debug, + ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get())); // try to get already loaded document - txLoadedDocumentInfo* info = mLoadedDocuments.LookupOrAdd(aUri); + txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri); + if (!entry) { + return nullptr; + } - if (!info->mDocument && !info->LoadingFailed()) { + if (!entry->mDocument && !entry->LoadingFailed()) { // open URI nsAutoString errMsg; - info->mLoadResult = - txParseDocumentFromURI(aUri, mLoadingDocument, - errMsg, getter_Transfers(info->mDocument)); + // XXX we should get the loader from the actual node + // triggering the load, but this will do for the time being + entry->mLoadResult = + txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument, + errMsg, getter_Transfers(entry->mDocument)); - if (info->LoadingFailed()) { - nsAutoCString spec; - aUri->GetSpec(spec); + if (entry->LoadingFailed()) { receiveError(NS_LITERAL_STRING("Couldn't load document '") + - NS_ConvertUTF8toUTF16(spec) + - NS_LITERAL_STRING("': ") + errMsg, - info->mLoadResult); + aUri + NS_LITERAL_STRING("': ") + errMsg, + entry->mLoadResult); } } - return info->mDocument; + return entry->mDocument; } nsresult diff --git a/dom/xslt/xslt/txExecutionState.h b/dom/xslt/xslt/txExecutionState.h index 9352f93aca75..1d4b29013993 100644 --- a/dom/xslt/xslt/txExecutionState.h +++ b/dom/xslt/xslt/txExecutionState.h @@ -17,19 +17,24 @@ #include "txStylesheet.h" #include "txXPathTreeWalker.h" #include "nsTArray.h" -#include "nsURIHashKey.h" class txAOutputHandlerFactory; class txAXMLEventHandler; class txInstruction; -class txLoadedDocumentInfo +class txLoadedDocumentEntry : public nsStringHashKey { public: - explicit txLoadedDocumentInfo() : mLoadResult(NS_OK) + explicit txLoadedDocumentEntry(KeyTypePointer aStr) : nsStringHashKey(aStr), + mLoadResult(NS_OK) { } - ~txLoadedDocumentInfo() + txLoadedDocumentEntry(const txLoadedDocumentEntry& aToCopy) + : nsStringHashKey(aToCopy) + { + NS_ERROR("We're horked."); + } + ~txLoadedDocumentEntry() { if (mDocument) { txXPathNodeUtils::release(mDocument); @@ -47,11 +52,11 @@ public: nsresult mLoadResult; }; -class txLoadedDocumentsHash : public nsClassHashtable +class txLoadedDocumentsHash : public nsTHashtable { public: txLoadedDocumentsHash() - : nsClassHashtable(4), + : nsTHashtable(4), mSourceDocument(nullptr) { } @@ -67,8 +72,7 @@ private: class txExecutionState : public txIMatchContext { public: - txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads, - nsIDocument* aLoaderDocument); + txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads); ~txExecutionState(); nsresult init(const txXPathNode& aNode, txOwningExpandedNameMap* aGlobalParams); @@ -103,7 +107,7 @@ public: // state-getting functions txIEvalContext* getEvalContext(); - const txXPathNode* retrieveDocument(nsIURI* aUri); + const txXPathNode* retrieveDocument(const nsAString& aUri); nsresult getKeyNodes(const txExpandedName& aKeyName, const txXPathNode& aRoot, const nsAString& aKeyValue, bool aIndexIfNotFound, @@ -157,7 +161,6 @@ private: //Document* mRTFDocument; txOwningExpandedNameMap* mGlobalParams; - nsCOMPtr mLoadingDocument; txLoadedDocumentsHash mLoadedDocuments; txKeyHash mKeyHash; RefPtr mRecycler; diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index 2e4768fb3d3c..e00b354a4a44 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -257,16 +257,6 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) nsCOMPtr channel = do_QueryInterface(aRequest); - nsCOMPtr channelPrincipal; - nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( - channel, getter_AddRefs(channelPrincipal)); - mCompiler->setPrincipal(channelPrincipal); - - nsCOMPtr baseURI; - nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(baseURI)); - NS_ENSURE_SUCCESS(rv, rv); - mCompiler->setBaseURI(baseURI); - // check channel's charset... nsAutoCString charsetVal; nsAutoCString charset; @@ -385,8 +375,9 @@ public: TX_DECL_ACOMPILEOBSERVER NS_INLINE_DECL_REFCOUNTING(txCompileObserver) - nsresult startLoad(nsIURI* aUri, nsIPrincipal* aSourcePrincipal, - txStylesheetCompiler* aCompiler); + nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, + nsIPrincipal* aSourcePrincipal, + ReferrerPolicy aReferrerPolicy); private: RefPtr mProcessor; @@ -409,15 +400,30 @@ txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor, } nsresult -txCompileObserver::loadURI(nsIURI* aUri, - nsIPrincipal* aReferrerPrincipal, +txCompileObserver::loadURI(const nsAString& aUri, + const nsAString& aReferrerUri, + ReferrerPolicy aReferrerPolicy, txStylesheetCompiler* aCompiler) { - if (mProcessor->IsLoadDisabled() || !mLoaderDocument) { + if (mProcessor->IsLoadDisabled()) { return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR; } - return startLoad(aUri, aReferrerPrincipal, aCompiler); + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr referrerUri; + rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr referrerPrincipal; + rv = nsContentUtils::GetSecurityManager()-> + GetSimpleCodebasePrincipal(referrerUri, + getter_AddRefs(referrerPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + + return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy); } void @@ -435,11 +441,10 @@ txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, } nsresult -txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal, - txStylesheetCompiler* aCompiler) +txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, + nsIPrincipal* aReferrerPrincipal, + ReferrerPolicy aReferrerPolicy) { - MOZ_ASSERT(aReferrerPrincipal); - nsCOMPtr loadGroup = mLoaderDocument->GetDocumentLoadGroup(); if (!loadGroup) { return NS_ERROR_FAILURE; @@ -468,8 +473,7 @@ txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal, nsCOMPtr referrerURI; aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI)); if (referrerURI) { - httpChannel->SetReferrerWithPolicy(referrerURI, - mLoaderDocument->GetReferrerPolicy()); + httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy); } } @@ -477,6 +481,7 @@ txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal, NS_ENSURE_SUCCESS(rv, rv); RefPtr sink = new txStylesheetSink(aCompiler, parser); + NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY); channel->SetNotificationCallbacks(sink); @@ -489,26 +494,24 @@ txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal, nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, - nsIDocument* aLoaderDocument) + nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy) { - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsAutoCString spec; - aUri->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("TX_LoadSheet: %s\n", spec.get())); - } + nsIPrincipal* principal = aLoaderDocument->NodePrincipal(); + + nsAutoCString spec; + aUri->GetSpec(spec); + MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get())); RefPtr observer = new txCompileObserver(aProcessor, aLoaderDocument); - - nsAutoCString fragment; - aUri->GetRef(fragment); + NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY); RefPtr compiler = - new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), observer); + new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy, + observer); + NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - return observer->startLoad(aUri, aLoaderDocument->NodePrincipal(), - compiler); + return observer->startLoad(aUri, compiler, principal, aReferrerPolicy); } /** @@ -582,11 +585,7 @@ handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler) class txSyncCompileObserver final : public txACompileObserver { public: - explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor, - nsIDocument* aLoaderDocument) - : mProcessor(aProcessor), - mLoaderDocument(aLoaderDocument) - {} + explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor); TX_DECL_ACOMPILEOBSERVER NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver) @@ -598,43 +597,60 @@ private: } RefPtr mProcessor; - nsCOMPtr mLoaderDocument; }; +txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor) + : mProcessor(aProcessor) +{ +} + nsresult -txSyncCompileObserver::loadURI(nsIURI* aUri, - nsIPrincipal* aReferrerPrincipal, +txSyncCompileObserver::loadURI(const nsAString& aUri, + const nsAString& aReferrerUri, + ReferrerPolicy aReferrerPolicy, txStylesheetCompiler* aCompiler) { - MOZ_ASSERT(aReferrerPrincipal); - - if (mProcessor->IsLoadDisabled() || !mLoaderDocument) { + if (mProcessor->IsLoadDisabled()) { return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR; } - nsAutoSyncOperation sync(mLoaderDocument); + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr referrerUri; + rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr referrerPrincipal; + rv = nsContentUtils::GetSecurityManager()-> + GetSimpleCodebasePrincipal(referrerUri, + getter_AddRefs(referrerPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + + // This is probably called by js, a loadGroup for the channel doesn't + // make sense. + nsCOMPtr source; + if (mProcessor) { + source = + do_QueryInterface(mProcessor->GetSourceContentModel()); + } + nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr); nsCOMPtr document; - nsCOMPtr loadGroup = mLoaderDocument->GetDocumentLoadGroup(); - nsresult rv = - nsSyncLoadService::LoadDocument(aUri, - nsIContentPolicy::TYPE_XSLT, - aReferrerPrincipal, - nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, - loadGroup, - false, - mLoaderDocument->GetReferrerPolicy(), - getter_AddRefs(document)); + rv = nsSyncLoadService::LoadDocument(uri, nsIContentPolicy::TYPE_XSLT, + referrerPrincipal, + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + nullptr, false, + aReferrerPolicy, + getter_AddRefs(document)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr doc = do_QueryInterface(document); - nsCOMPtr baseURI = doc->GetBaseURI(); - aCompiler->setBaseURI(baseURI); - aCompiler->setPrincipal(doc->NodePrincipal()); rv = handleNode(doc, aCompiler); if (NS_FAILED(rv)) { nsAutoCString spec; - aUri->GetSpec(spec); + uri->GetSpec(spec); aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get()); return rv; } @@ -651,20 +667,46 @@ void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler, } nsresult -TX_CompileStylesheet(nsINode* aNode, - nsIDocument* aLoaderDocument, - txMozillaXSLTProcessor* aProcessor, +TX_CompileStylesheet(nsINode* aNode, txMozillaXSLTProcessor* aProcessor, txStylesheet** aStylesheet) { + // If we move GetBaseURI to nsINode this can be simplified. + nsCOMPtr doc = aNode->OwnerDoc(); + + nsCOMPtr uri; + if (aNode->IsNodeOfType(nsINode::eCONTENT)) { + uri = static_cast(aNode)->GetBaseURI(); + } + else { + NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc"); + uri = static_cast(aNode)->GetBaseURI(); + } + NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); + + nsAutoCString spec; + uri->GetSpec(spec); + NS_ConvertUTF8toUTF16 baseURI(spec); + + nsIURI* docUri = doc->GetDocumentURI(); + NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE); + + // We need to remove the ref, a URI with a ref would mean that we have an + // embedded stylesheet. + docUri->CloneIgnoringRef(getter_AddRefs(uri)); + NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); + + uri->GetSpec(spec); + NS_ConvertUTF8toUTF16 stylesheetURI(spec); + RefPtr obs = - new txSyncCompileObserver(aProcessor, aLoaderDocument); + new txSyncCompileObserver(aProcessor); + NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY); RefPtr compiler = - new txStylesheetCompiler(EmptyString(), obs); + new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs); + NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - nsCOMPtr baseURI = aNode->GetBaseURI(); compiler->setBaseURI(baseURI); - compiler->setPrincipal(aNode->NodePrincipal()); nsresult rv = handleNode(aNode, compiler); if (NS_FAILED(rv)) { diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp index c6f277a009a1..6e0c0945b58c 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp @@ -361,13 +361,6 @@ txMozillaXSLTProcessor::txMozillaXSLTProcessor(nsISupports* aOwner) { } -NS_IMETHODIMP -txMozillaXSLTProcessor::Init(nsISupports* aOwner) -{ - mOwner = aOwner; - return NS_OK; -} - txMozillaXSLTProcessor::~txMozillaXSLTProcessor() { if (mStylesheetDocument) { @@ -611,7 +604,7 @@ txMozillaXSLTProcessor::ImportStylesheet(nsIDOMNode *aStyle) styleNode->IsNodeOfType(nsINode::eDOCUMENT)), NS_ERROR_INVALID_ARG); - nsresult rv = TX_CompileStylesheet(styleNode, getLoaderDoc(), this, + nsresult rv = TX_CompileStylesheet(styleNode, this, getter_AddRefs(mStylesheet)); // XXX set up exception context, bug 204658 NS_ENSURE_SUCCESS(rv, rv); @@ -666,7 +659,7 @@ txMozillaXSLTProcessor::TransformToDoc(nsIDOMDocument **aResult, sourceDOMDocument = do_QueryInterface(mSource); } - txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc()); + txExecutionState es(mStylesheet, IsLoadDisabled()); // XXX Need to add error observers @@ -734,7 +727,7 @@ txMozillaXSLTProcessor::TransformToFragment(nsIDOMNode *aSource, return NS_ERROR_OUT_OF_MEMORY; } - txExecutionState es(mStylesheet, IsLoadDisabled(), getLoaderDoc()); + txExecutionState es(mStylesheet, IsLoadDisabled()); // XXX Need to add error observers @@ -1043,7 +1036,12 @@ NS_IMETHODIMP txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri, nsIDocument* aLoaderDocument) { - nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument); + mozilla::net::ReferrerPolicy refpol = mozilla::net::RP_Default; + if (mStylesheetDocument) { + refpol = mStylesheetDocument->GetReferrerPolicy(); + } + + nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol); if (NS_FAILED(rv) && mObserver) { // This is most likely a network or security error, just // use the uri as context. @@ -1200,24 +1198,6 @@ txMozillaXSLTProcessor::notifyError() mObserver->OnTransformDone(mTransformResult, document); } -nsIDocument* -txMozillaXSLTProcessor::getLoaderDoc() -{ - if (mOwner) { - nsCOMPtr win = do_QueryInterface(mOwner); - if (win) { - return win->GetExtantDoc(); - } - } - - if (mSource) { - nsCOMPtr node = do_QueryInterface(mSource); - return node->OwnerDoc(); - } - - return nullptr; -} - nsresult txMozillaXSLTProcessor::ensureStylesheet() { @@ -1232,8 +1212,7 @@ txMozillaXSLTProcessor::ensureStylesheet() style = mStylesheetDocument; } - return TX_CompileStylesheet(style, getLoaderDoc(), this, - getter_AddRefs(mStylesheet)); + return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet)); } void diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.h b/dom/xslt/xslt/txMozillaXSLTProcessor.h index 1cf381a873b6..6fa2ab1338c2 100644 --- a/dom/xslt/xslt/txMozillaXSLTProcessor.h +++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h @@ -166,8 +166,6 @@ private: void notifyError(); nsresult ensureStylesheet(); - nsIDocument* getLoaderDoc(); - nsCOMPtr mOwner; RefPtr mStylesheet; @@ -187,10 +185,10 @@ private: }; extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, - nsIDocument* aLoaderDocument); + nsIDocument* aLoaderDocument, + mozilla::net::ReferrerPolicy aReferrerPolicy); extern nsresult TX_CompileStylesheet(nsINode* aNode, - nsIDocument* aLoaderDocument, txMozillaXSLTProcessor* aProcessor, txStylesheet** aStylesheet); diff --git a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp index 295604cd1197..f2aa10a796d3 100644 --- a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp +++ b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp @@ -20,7 +20,6 @@ #include "txNamespaceMap.h" #include "txURIUtils.h" #include "txXSLTFunctions.h" -#include "nsNetUtil.h" using namespace mozilla; @@ -757,12 +756,10 @@ txFnStartImport(int32_t aNamespaceID, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr, - aState.mElementContext->mBaseURI); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aState.loadImportedStylesheet(uri, importPtr->mFrame); + nsAutoString absUri; + URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, + absUri); + rv = aState.loadImportedStylesheet(absUri, importPtr->mFrame); NS_ENSURE_SUCCESS(rv, rv); return aState.pushHandlerTable(gTxIgnoreHandler); @@ -790,12 +787,10 @@ txFnStartInclude(int32_t aNamespaceID, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, nullptr, - aState.mElementContext->mBaseURI); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aState.loadIncludedStylesheet(uri); + nsAutoString absUri; + URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, + absUri); + rv = aState.loadIncludedStylesheet(absUri); NS_ENSURE_SUCCESS(rv, rv); return aState.pushHandlerTable(gTxIgnoreHandler); diff --git a/dom/xslt/xslt/txStylesheetCompiler.cpp b/dom/xslt/xslt/txStylesheetCompiler.cpp index bf37a9fb2408..d22ba41efb97 100644 --- a/dom/xslt/xslt/txStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txStylesheetCompiler.cpp @@ -23,29 +23,31 @@ #include "nsICategoryManager.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" -#include "nsIURI.h" using namespace mozilla; using mozilla::net::ReferrerPolicy; -txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment, +txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, + ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aFragment, nullptr, nullptr); + mStatus = init(aStylesheetURI, aReferrerPolicy, nullptr, nullptr); } -txStylesheetCompiler::txStylesheetCompiler(const nsString& aFragment, +txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition, + ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { - mStatus = init(aFragment, aStylesheet, aInsertPosition); + mStatus = init(aStylesheetURI, aReferrerPolicy, aStylesheet, + aInsertPosition); } void -txStylesheetCompiler::setBaseURI(nsIURI* aBaseURI) +txStylesheetCompiler::setBaseURI(const nsString& aBaseURI) { NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(), "Execution already started"); @@ -57,16 +59,6 @@ txStylesheetCompiler::setBaseURI(nsIURI* aBaseURI) mElementContext->mBaseURI = aBaseURI; } -void -txStylesheetCompiler::setPrincipal(nsIPrincipal* aPrincipal) -{ - if (NS_FAILED(mStatus)) { - return; - } - - mStylesheetPrincipal = aPrincipal; -} - nsresult txStylesheetCompiler::startElement(int32_t aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, @@ -225,11 +217,9 @@ txStylesheetCompiler::startElementInternal(int32_t aNamespaceID, rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), attr->mValue, - nullptr, mElementContext->mBaseURI); - NS_ENSURE_SUCCESS(rv, rv); - mElementContext->mBaseURI = uri.forget(); + nsAutoString uri; + URIUtils::resolveHref(attr->mValue, mElementContext->mBaseURI, uri); + mElementContext->mBaseURI = uri; } // extension-element-prefixes @@ -381,15 +371,9 @@ txStylesheetCompiler::characters(const nsAString& aStr) nsresult txStylesheetCompiler::doneLoading() { - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsCOMPtr uri; - mStylesheetPrincipal->GetURI(getter_AddRefs(uri)); - nsAutoCString spec; - uri->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::doneLoading: %s\n", - spec.get())); - } + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::doneLoading: %s\n", + NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); if (NS_FAILED(mStatus)) { return mStatus; } @@ -403,17 +387,11 @@ void txStylesheetCompiler::cancel(nsresult aError, const char16_t *aErrorText, const char16_t *aParam) { - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsCOMPtr uri; - mStylesheetPrincipal->GetURI(getter_AddRefs(uri)); - nsAutoCString spec; - uri->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::cancel: %s, module: %d, code %d\n", - spec.get(), - NS_ERROR_GET_MODULE(aError), - NS_ERROR_GET_CODE(aError))); - } + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::cancel: %s, module: %d, code %d\n", + NS_LossyConvertUTF16toASCII(mStylesheetURI).get(), + NS_ERROR_GET_MODULE(aError), + NS_ERROR_GET_CODE(aError))); if (NS_SUCCEEDED(mStatus)) { mStatus = aError; } @@ -433,30 +411,20 @@ txStylesheetCompiler::getStylesheet() } nsresult -txStylesheetCompiler::loadURI(nsIURI* aUri, - nsIPrincipal* aReferrerPrincipal, +txStylesheetCompiler::loadURI(const nsAString& aUri, + const nsAString& aReferrerUri, + ReferrerPolicy aReferrerPolicy, txStylesheetCompiler* aCompiler) { - nsCOMPtr stylesheetURI; - mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); - - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsAutoCString stylesheetSpec; - stylesheetURI->GetSpec(stylesheetSpec); - nsAutoCString uriSpec; - aUri->GetSpec(uriSpec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("Compiler::loadURI forwards %s thru %s\n", - uriSpec.get(), - stylesheetSpec.get())); - } - - bool equals; - if (NS_FAILED(stylesheetURI->Equals(aUri, &equals)) || equals) { + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("Compiler::loadURI forwards %s thru %s\n", + NS_LossyConvertUTF16toASCII(aUri).get(), + NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); + if (mStylesheetURI.Equals(aUri)) { return NS_ERROR_XSLT_LOAD_RECURSION; } return mObserver ? - mObserver->loadURI(aUri, aReferrerPrincipal, aCompiler) : + mObserver->loadURI(aUri, aReferrerUri, aReferrerPolicy, aCompiler) : NS_ERROR_FAILURE; } @@ -566,20 +534,27 @@ txStylesheetCompilerState::txStylesheetCompilerState(txACompileObserver* aObserv } nsresult -txStylesheetCompilerState::init(const nsString& aFragment, +txStylesheetCompilerState::init(const nsAString& aStylesheetURI, + ReferrerPolicy aReferrerPolicy, txStylesheet* aStylesheet, txListIterator* aInsertPosition) { NS_ASSERTION(!aStylesheet || aInsertPosition, "must provide insertposition if loading subsheet"); - + mStylesheetURI = aStylesheetURI; + mReferrerPolicy = aReferrerPolicy; // Check for fragment identifier of an embedded stylesheet. - if (!aFragment.IsEmpty()) { - // This is really an embedded stylesheet, not just a - // "url#". We may want to unescape the fragment. - mTarget = aFragment; - mEmbedStatus = eNeedEmbed; - mHandlerTable = gTxEmbedHandler; + int32_t fragment = aStylesheetURI.FindChar('#') + 1; + if (fragment > 0) { + int32_t fragmentLength = aStylesheetURI.Length() - fragment; + if (fragmentLength > 0) { + // This is really an embedded stylesheet, not just a + // "url#". We may want to unescape the fragment. + mTarget = Substring(aStylesheetURI, (uint32_t)fragment, + fragmentLength); + mEmbedStatus = eNeedEmbed; + mHandlerTable = gTxEmbedHandler; + } } nsresult rv = NS_OK; if (aStylesheet) { @@ -598,7 +573,8 @@ txStylesheetCompilerState::init(const nsString& aFragment, mIsTopCompiler = true; } - mElementContext = new txElementContext(); + mElementContext = new txElementContext(aStylesheetURI); + NS_ENSURE_TRUE(mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY); // Push the "old" txElementContext rv = pushObject(0); @@ -761,25 +737,18 @@ txStylesheetCompilerState::addInstruction(nsAutoPtr&& aInstructio } nsresult -txStylesheetCompilerState::loadIncludedStylesheet(nsIURI* aURI) +txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI) { - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsAutoCString spec; - aURI->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("CompilerState::loadIncludedStylesheet: %s\n", - spec.get())); - } - - nsCOMPtr stylesheetURI; - mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); - bool equals; - if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) { + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("CompilerState::loadIncludedStylesheet: %s\n", + NS_LossyConvertUTF16toASCII(aURI).get())); + if (mStylesheetURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); nsAutoPtr item(new txDummyItem); + NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY); nsresult rv = mToplevelIterator.addBefore(item); NS_ENSURE_SUCCESS(rv, rv); @@ -791,21 +760,19 @@ txStylesheetCompilerState::loadIncludedStylesheet(nsIURI* aURI) txACompileObserver* observer = static_cast(this); - nsAutoCString fragment; - aURI->GetRef(fragment); - RefPtr compiler = - new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), - mStylesheet, - &mToplevelIterator, - observer); + new txStylesheetCompiler(aURI, mStylesheet, &mToplevelIterator, + mReferrerPolicy, observer); + NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); // step forward before calling the observer in case of syncronous loading mToplevelIterator.next(); - mChildCompilerList.AppendElement(compiler); + if (mChildCompilerList.AppendElement(compiler) == nullptr) { + return NS_ERROR_OUT_OF_MEMORY; + } - rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler); + rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } @@ -814,21 +781,13 @@ txStylesheetCompilerState::loadIncludedStylesheet(nsIURI* aURI) } nsresult -txStylesheetCompilerState::loadImportedStylesheet(nsIURI* aURI, +txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI, txStylesheet::ImportFrame* aFrame) { - if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Info)) { - nsAutoCString spec; - aURI->GetSpec(spec); - MOZ_LOG(txLog::xslt, LogLevel::Info, - ("CompilerState::loadImportedStylesheet: %s\n", - spec.get())); - } - - nsCOMPtr stylesheetURI; - mStylesheetPrincipal->GetURI(getter_AddRefs(stylesheetURI)); - bool equals; - if (NS_FAILED(stylesheetURI->Equals(aURI, &equals)) || equals) { + MOZ_LOG(txLog::xslt, LogLevel::Info, + ("CompilerState::loadImportedStylesheet: %s\n", + NS_LossyConvertUTF16toASCII(aURI).get())); + if (mStylesheetURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); @@ -838,18 +797,17 @@ txStylesheetCompilerState::loadImportedStylesheet(nsIURI* aURI, txACompileObserver* observer = static_cast(this); - nsAutoCString fragment; - aURI->GetRef(fragment); - RefPtr compiler = - new txStylesheetCompiler(NS_ConvertUTF8toUTF16(fragment), - mStylesheet, - &iter, + new txStylesheetCompiler(aURI, mStylesheet, &iter, mReferrerPolicy, observer); + NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY); - mChildCompilerList.AppendElement(compiler); + if (mChildCompilerList.AppendElement(compiler) == nullptr) { + return NS_ERROR_OUT_OF_MEMORY; + } - nsresult rv = mObserver->loadURI(aURI, mStylesheetPrincipal, compiler); + nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, + compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } @@ -1110,7 +1068,9 @@ extern bool TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID) { RefPtr compiler = - new txStylesheetCompiler(EmptyString(), nullptr); + new txStylesheetCompiler(EmptyString(), + mozilla::net::RP_Default, nullptr); + NS_ENSURE_TRUE(compiler, false); nsAutoPtr fnCall; @@ -1154,9 +1114,10 @@ txStylesheetCompilerState::shutdown() sXPCOMFunctionMappings = nullptr; } -txElementContext::txElementContext() +txElementContext::txElementContext(const nsAString& aBaseURI) : mPreserveWhitespace(false), mForwardsCompatibleParsing(true), + mBaseURI(aBaseURI), mMappings(new txNamespaceMap), mDepth(0) { diff --git a/dom/xslt/xslt/txStylesheetCompiler.h b/dom/xslt/xslt/txStylesheetCompiler.h index b15272160ca0..7fb0f05d6061 100644 --- a/dom/xslt/xslt/txStylesheetCompiler.h +++ b/dom/xslt/xslt/txStylesheetCompiler.h @@ -32,12 +32,12 @@ class txInScopeVariable; class txElementContext : public txObject { public: - explicit txElementContext(); + explicit txElementContext(const nsAString& aBaseURI); txElementContext(const txElementContext& aOther); bool mPreserveWhitespace; bool mForwardsCompatibleParsing; - nsCOMPtr mBaseURI; + nsString mBaseURI; RefPtr mMappings; nsTArray mInstructionNamespaces; int32_t mDepth; @@ -49,8 +49,9 @@ public: NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; NS_IMETHOD_(MozExternalRefCountType) Release() = 0; - virtual nsresult loadURI(nsIURI* aUri, - nsIPrincipal* aReferrerPrincipal, + virtual nsresult loadURI(const nsAString& aUri, + const nsAString& aReferrerUri, + mozilla::net::ReferrerPolicy aReferrerPolicy, txStylesheetCompiler* aCompiler) = 0; virtual void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, @@ -59,8 +60,8 @@ public: }; #define TX_DECL_ACOMPILEOBSERVER \ - nsresult loadURI(nsIURI* aUri, \ - nsIPrincipal* aReferrerPrincipal, \ + nsresult loadURI(const nsAString& aUri, const nsAString& aReferrerUri, \ + mozilla::net::ReferrerPolicy aReferrerPolicy, \ txStylesheetCompiler* aCompiler); \ void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, \ const char16_t *aErrorText = nullptr, \ @@ -72,8 +73,9 @@ public: explicit txStylesheetCompilerState(txACompileObserver* aObserver); ~txStylesheetCompilerState(); - nsresult init(const nsString& aFragment, txStylesheet* aStylesheet, - txListIterator* aInsertPosition); + nsresult init(const nsAString& aStylesheetURI, + mozilla::net::ReferrerPolicy aReferrerPolicy, + txStylesheet* aStylesheet, txListIterator* aInsertPosition); // Embedded stylesheets state bool handleEmbeddedSheet() @@ -114,8 +116,8 @@ public: nsresult openInstructionContainer(txInstructionContainer* aContainer); void closeInstructionContainer(); nsresult addInstruction(nsAutoPtr&& aInstruction); - nsresult loadIncludedStylesheet(nsIURI* aURI); - nsresult loadImportedStylesheet(nsIURI* aURI, + nsresult loadIncludedStylesheet(const nsAString& aURI); + nsresult loadImportedStylesheet(const nsAString& aURI, txStylesheet::ImportFrame* aFrame); // misc @@ -164,7 +166,6 @@ public: uint16_t mDisAllowed; protected: - nsCOMPtr mStylesheetPrincipal; RefPtr mObserver; nsTArray mInScopeVariables; nsTArray mChildCompilerList; @@ -177,6 +178,7 @@ protected: eInEmbed, eHasEmbed } mEmbedStatus; + nsString mStylesheetURI; bool mIsTopCompiler; bool mDoneWithThisStylesheet; txStack mObjectStack; @@ -187,6 +189,7 @@ private: txInstruction** mNextInstrPtr; txListIterator mToplevelIterator; nsTArray mGotoTargetPointers; + mozilla::net::ReferrerPolicy mReferrerPolicy; }; struct txStylesheetAttr @@ -204,15 +207,16 @@ public: friend class txStylesheetCompilerState; friend bool TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID); - txStylesheetCompiler(const nsString& aFragment, + txStylesheetCompiler(const nsAString& aStylesheetURI, + mozilla::net::ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver); - txStylesheetCompiler(const nsString& aFragment, + txStylesheetCompiler(const nsAString& aStylesheetURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition, + mozilla::net::ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver); - void setBaseURI(nsIURI* aBaseURI); - void setPrincipal(nsIPrincipal* aPrincipal); + void setBaseURI(const nsString& aBaseURI); nsresult startElement(int32_t aNamespaceID, nsIAtom* aLocalName, nsIAtom* aPrefix, txStylesheetAttr* aAttributes, diff --git a/dom/xslt/xslt/txXSLTFunctions.h b/dom/xslt/xslt/txXSLTFunctions.h index 3ef80f09507e..1bc0f9961eb0 100644 --- a/dom/xslt/xslt/txXSLTFunctions.h +++ b/dom/xslt/xslt/txXSLTFunctions.h @@ -23,14 +23,12 @@ public: /** * Creates a new document() function call **/ - explicit DocumentFunctionCall(nsIURI* aBaseURI) - : mBaseURI(aBaseURI) - {} + explicit DocumentFunctionCall(const nsAString& aBaseURI); TX_DECL_FUNCTION private: - nsCOMPtr mBaseURI; + nsString mBaseURI; }; /* From c54ba8a1ccdccc59c83e043f97e955193daa4f35 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:56:07 +0100 Subject: [PATCH 15/24] Bug 1226928 - new preferences for content-signing on remote about:newtab, r=mconley --- browser/app/profile/firefox.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 8393dbb0fdc0..5dd5e3cd86aa 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1380,6 +1380,12 @@ pref("browser.newtabpage.remote", false); // Toggles endpoints allowed for remote newtab communications pref("browser.newtabpage.remote.mode", "production"); +// content-signature tests for remote newtab +pref("browser.newtabpage.remote.content-signing-test", false); + +// verification keys for remote-hosted newtab page +pref("browser.newtabpage.remote.keys", ""); + // Enable the DOM fullscreen API. pref("full-screen-api.enabled", true); From de6c5e883ac10cbf8ae9e1b81e0ade0c9222d919 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:56:25 +0100 Subject: [PATCH 16/24] Bug 1226928 - remove Wshadow warning in ScopedNSSTypes, r=keeler --- security/manager/ssl/ScopedNSSTypes.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/security/manager/ssl/ScopedNSSTypes.h b/security/manager/ssl/ScopedNSSTypes.h index 85c1ec6f7df7..a3376b15967c 100644 --- a/security/manager/ssl/ScopedNSSTypes.h +++ b/security/manager/ssl/ScopedNSSTypes.h @@ -149,9 +149,9 @@ class Digest public: Digest() { - item.type = siBuffer; - item.data = buf; - item.len = 0; + mItem.type = siBuffer; + mItem.data = mItemBuf; + mItem.len = 0; } nsresult DigestBuf(SECOidTag hashAlg, const uint8_t * buf, uint32_t len) @@ -161,7 +161,7 @@ public: } nsresult rv = SetLength(hashAlg); NS_ENSURE_SUCCESS(rv, rv); - return MapSECStatus(PK11_HashBuf(hashAlg, item.data, buf, + return MapSECStatus(PK11_HashBuf(hashAlg, mItem.data, buf, static_cast(len))); } @@ -170,14 +170,14 @@ public: nsresult rv = SetLength(hashAlg); NS_ENSURE_SUCCESS(rv, rv); uint32_t len; - rv = MapSECStatus(PK11_DigestFinal(context, item.data, &len, item.len)); + rv = MapSECStatus(PK11_DigestFinal(context, mItem.data, &len, mItem.len)); NS_ENSURE_SUCCESS(rv, rv); context = nullptr; - NS_ENSURE_TRUE(len == item.len, NS_ERROR_UNEXPECTED); + NS_ENSURE_TRUE(len == mItem.len, NS_ERROR_UNEXPECTED); return NS_OK; } - const SECItem & get() const { return item; } + const SECItem & get() const { return mItem; } private: nsresult SetLength(SECOidTag hashType) @@ -190,10 +190,10 @@ private: #endif switch (hashType) { - case SEC_OID_SHA1: item.len = SHA1_LENGTH; break; - case SEC_OID_SHA256: item.len = SHA256_LENGTH; break; - case SEC_OID_SHA384: item.len = SHA384_LENGTH; break; - case SEC_OID_SHA512: item.len = SHA512_LENGTH; break; + case SEC_OID_SHA1: mItem.len = SHA1_LENGTH; break; + case SEC_OID_SHA256: mItem.len = SHA256_LENGTH; break; + case SEC_OID_SHA384: mItem.len = SHA384_LENGTH; break; + case SEC_OID_SHA512: mItem.len = SHA512_LENGTH; break; default: return NS_ERROR_INVALID_ARG; } @@ -204,8 +204,8 @@ private: return NS_OK; } - uint8_t buf[HASH_LENGTH_MAX]; - SECItem item; + uint8_t mItemBuf[HASH_LENGTH_MAX]; + SECItem mItem; }; // Deprecated: use the equivalent UniquePtr templates instead. From bd54ab19d39865e26d54164d6c338fb8ea50f17e Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:56:35 +0100 Subject: [PATCH 17/24] Bug 1226928 - signature verification for content-signing, r=keeler,mayhemer --- dom/security/ContentVerifier.cpp | 375 ++++++++++++++++++++++++++ dom/security/ContentVerifier.h | 98 +++++++ dom/security/moz.build | 2 + security/manager/ssl/ScopedNSSTypes.h | 8 + security/manager/ssl/moz.build | 1 + xpcom/base/ErrorList.h | 4 + 6 files changed, 488 insertions(+) create mode 100644 dom/security/ContentVerifier.cpp create mode 100644 dom/security/ContentVerifier.h diff --git a/dom/security/ContentVerifier.cpp b/dom/security/ContentVerifier.cpp new file mode 100644 index 000000000000..9a83f9143032 --- /dev/null +++ b/dom/security/ContentVerifier.cpp @@ -0,0 +1,375 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ContentVerifier.h" + +#include "mozilla/fallible.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPtr.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsIInputStream.h" +#include "nsIRequest.h" +#include "nssb64.h" +#include "nsSecurityHeaderParser.h" +#include "nsServiceManagerUtils.h" +#include "nsStringStream.h" +#include "nsThreadUtils.h" + +using namespace mozilla; + +static LazyLogModule gContentVerifierPRLog("ContentVerifier"); +#define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args) + +// Content-Signature prefix +const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00"); + +NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports); + +nsresult +ContentVerifier::Init(const nsAString& aContentSignatureHeader) +{ + mVks = Preferences::GetString("browser.newtabpage.remote.keys"); + + if (aContentSignatureHeader.IsEmpty() || mVks.IsEmpty()) { + CSV_LOG( + ("Content-Signature header and verification keys must not be empty!\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader); + NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE); + return CreateContext(); +} + +/** + * Implement nsIStreamListener + * We buffer the entire content here and kick off verification + */ +NS_METHOD +AppendNextSegment(nsIInputStream* aInputStream, void* aClosure, + const char* aRawSegment, uint32_t aToOffset, uint32_t aCount, + uint32_t* outWrittenCount) +{ + FallibleTArray* decodedData = + static_cast*>(aClosure); + nsAutoCString segment(aRawSegment, aCount); + if (!decodedData->AppendElement(segment, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + *outWrittenCount = aCount; + return NS_OK; +} + +NS_IMETHODIMP +ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) +{ + return NS_OK; +} + +NS_IMETHODIMP +ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, + nsresult aStatus) +{ + // Verify the content: + // If this fails, we return an invalid signature error to load a fallback page. + // If everthing is good, we return a new stream to the next listener and kick + // that one of. + CSV_LOG(("VerifySignedContent, b64signature: %s\n", mSignature.get())); + CSV_LOG(("VerifySignedContent, key: \n[\n%s\n]\n", mKey.get())); + bool verified = false; + nsresult rv = End(&verified); + if (NS_FAILED(rv) || !verified || NS_FAILED(aStatus)) { + // cancel the request and return error + if (NS_FAILED(aStatus)) { + rv = aStatus; + } else { + rv = NS_ERROR_INVALID_SIGNATURE; + } + CSV_LOG(("failed to verify content\n")); + mNextListener->OnStartRequest(aRequest, aContext); + mNextListener->OnStopRequest(aRequest, aContext, rv); + return NS_ERROR_INVALID_SIGNATURE; + } + CSV_LOG(("Successfully verified content signature.\n")); + + // start the next listener + rv = mNextListener->OnStartRequest(aRequest, aContext); + if (NS_SUCCEEDED(rv)) { + // We emptied aInStr so we have to create a new one from buf to hand it + // to the consuming listener. + for (uint32_t i = 0; i < mContent.Length(); ++i) { + nsCOMPtr oInStr; + rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]); + if (NS_FAILED(rv)) { + break; + } + // let the next listener know that there is data in oInStr + rv = mNextListener->OnDataAvailable(aRequest, aContext, oInStr, 0, + mContent[i].Length()); + if (NS_FAILED(rv)) { + break; + } + } + } + + // propagate OnStopRequest and return + return mNextListener->OnStopRequest(aRequest, aContext, rv); +} + +NS_IMETHODIMP +ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, + nsIInputStream* aInputStream, uint64_t aOffset, + uint32_t aCount) +{ + // buffer the entire stream + uint32_t read; + nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount, + &read); + if (NS_FAILED(rv)) { + return rv; + } + + // update the signature verifier + return Update(mContent[mContent.Length()-1]); +} + +/** + * ContentVerifier logic and utils + */ + +nsresult +ContentVerifier::GetVerificationKey(const nsAString& aKeyId) +{ + // get verification keys from the pref and see if we have |aKeyId| + nsCharSeparatedTokenizer tokenizerVK(mVks, ';'); + while (tokenizerVK.hasMoreTokens()) { + nsDependentSubstring token = tokenizerVK.nextToken(); + nsCharSeparatedTokenizer tokenizerKey(token, '='); + nsString prefKeyId; + if (tokenizerKey.hasMoreTokens()) { + prefKeyId = tokenizerKey.nextToken(); + } + nsString key; + if (tokenizerKey.hasMoreTokens()) { + key = tokenizerKey.nextToken(); + } + if (prefKeyId.Equals(aKeyId)) { + mKey.Assign(NS_ConvertUTF16toUTF8(key)); + return NS_OK; + } + } + + // we didn't find the appropriate key + return NS_ERROR_INVALID_SIGNATURE; +} + +nsresult +ContentVerifier::ParseContentSignatureHeader( + const nsAString& aContentSignatureHeader) +{ + // We only support p384 ecdsa according to spec + NS_NAMED_LITERAL_CSTRING(keyid_var, "keyid"); + NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa"); + + nsAutoString contentSignature; + nsAutoString keyId; + nsAutoCString header = NS_ConvertUTF16toUTF8(aContentSignatureHeader); + nsSecurityHeaderParser parser(header.get()); + nsresult rv = parser.Parse(); + if (NS_FAILED(rv)) { + CSV_LOG(("ContentVerifier: could not parse ContentSignature header\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + LinkedList* directives = parser.GetDirectives(); + + for (nsSecurityHeaderDirective* directive = directives->getFirst(); + directive != nullptr; directive = directive->getNext()) { + CSV_LOG(("ContentVerifier: found directive %s\n", directive->mName.get())); + if (directive->mName.Length() == keyid_var.Length() && + directive->mName.EqualsIgnoreCase(keyid_var.get(), + keyid_var.Length())) { + if (!keyId.IsEmpty()) { + CSV_LOG(("ContentVerifier: found two keyIds\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + CSV_LOG(("ContentVerifier: found a keyid directive\n")); + keyId = NS_ConvertUTF8toUTF16(directive->mValue); + rv = GetVerificationKey(keyId); + NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE); + } + if (directive->mName.Length() == signature_var.Length() && + directive->mName.EqualsIgnoreCase(signature_var.get(), + signature_var.Length())) { + if (!contentSignature.IsEmpty()) { + CSV_LOG(("ContentVerifier: found two ContentSignatures\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + CSV_LOG(("ContentVerifier: found a ContentSignature directive\n")); + contentSignature = NS_ConvertUTF8toUTF16(directive->mValue); + mSignature = directive->mValue; + } + } + + // we have to ensure that we found a key and a signature at this point + if (mKey.IsEmpty()) { + CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find " + "an appropriate key.\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + if (mSignature.IsEmpty()) { + CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find " + "a signature.\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + return NS_OK; +} + +/** + * Parse signature, public key, and algorithm data for input to verification + * functions in VerifyData and CreateContext. + * + * https://datatracker.ietf.org/doc/draft-thomson-http-content-signature/ + * If aSignature is a content signature, the function returns + * NS_ERROR_INVALID_SIGNATURE if anything goes wrong. Only p384 with sha384 + * is supported and aSignature is a raw signature (r||s). + */ +nsresult +ContentVerifier::ParseInput(ScopedSECKEYPublicKey& aPublicKeyOut, + ScopedSECItem& aSignatureItemOut, + SECOidTag& aOidOut, + const nsNSSShutDownPreventionLock&) +{ + // Base 64 decode the key + ScopedSECItem keyItem(::SECITEM_AllocItem(nullptr, nullptr, 0)); + if (!keyItem || + !NSSBase64_DecodeBuffer(nullptr, keyItem, + mKey.get(), + mKey.Length())) { + return NS_ERROR_INVALID_SIGNATURE; + } + + // Extract the public key from the keyItem + ScopedCERTSubjectPublicKeyInfo pki( + SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem)); + if (!pki) { + return NS_ERROR_INVALID_SIGNATURE; + } + aPublicKeyOut = SECKEY_ExtractPublicKey(pki.get()); + + // in case we were not able to extract a key + if (!aPublicKeyOut) { + return NS_ERROR_INVALID_SIGNATURE; + } + + // Base 64 decode the signature + ScopedSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0)); + if (!rawSignatureItem || + !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem, + mSignature.get(), + mSignature.Length())) { + return NS_ERROR_INVALID_SIGNATURE; + } + + // get signature object and oid + if (!aSignatureItemOut) { + return NS_ERROR_INVALID_SIGNATURE; + } + // We have a raw ecdsa signature r||s so we have to DER-encode it first + // Note that we have to check rawSignatureItem->len % 2 here as + // DSAU_EncodeDerSigWithLen asserts this + if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) { + return NS_ERROR_INVALID_SIGNATURE; + } + if (DSAU_EncodeDerSigWithLen(aSignatureItemOut, rawSignatureItem, + rawSignatureItem->len) != SECSuccess) { + return NS_ERROR_INVALID_SIGNATURE; + } + aOidOut = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE; + + return NS_OK; +} + +/** + * Create a context for a signature verification. + * It sets signature, public key, and algorithms that should be used to verify + * the data. It also updates the verification buffer with the content-signature + * prefix. + */ +nsresult +ContentVerifier::CreateContext() +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_INVALID_SIGNATURE; + } + + // Bug 769521: We have to change b64 url to regular encoding as long as we + // don't have a b64 url decoder. This should change soon, but in the meantime + // we have to live with this. + mSignature.ReplaceChar('-', '+'); + mSignature.ReplaceChar('_', '/'); + + ScopedSECKEYPublicKey publicKey; + ScopedSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0)); + SECOidTag oid; + nsresult rv = ParseInput(publicKey, signatureItem, oid, locker); + if (NS_FAILED(rv)) { + return NS_ERROR_INVALID_SIGNATURE; + } + + mCx = UniqueVFYContext(VFY_CreateContext(publicKey, signatureItem, oid, NULL)); + if (!mCx) { + return NS_ERROR_INVALID_SIGNATURE; + } + + if (VFY_Begin(mCx.get()) != SECSuccess) { + return NS_ERROR_INVALID_SIGNATURE; + } + + // add the prefix to the verification buffer + return Update(kPREFIX); +} + +/** + * Add data to the context that should be verified. + */ +nsresult +ContentVerifier::Update(const nsACString& aData) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_INVALID_SIGNATURE; + } + + if (!aData.IsEmpty()) { + if (VFY_Update(mCx.get(), + (const unsigned char*)nsPromiseFlatCString(aData).get(), + aData.Length()) != SECSuccess) { + return NS_ERROR_INVALID_SIGNATURE; + } + } + + return NS_OK; +} + +/** + * Finish signature verification and return the result in _retval. + */ +nsresult +ContentVerifier::End(bool* _retval) +{ + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return NS_ERROR_INVALID_SIGNATURE; + } + + *_retval = (VFY_End(mCx.get()) == SECSuccess); + + return NS_OK; +} \ No newline at end of file diff --git a/dom/security/ContentVerifier.h b/dom/security/ContentVerifier.h new file mode 100644 index 000000000000..ae29418e2640 --- /dev/null +++ b/dom/security/ContentVerifier.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ContentVerifier_h +#define mozilla_dom_ContentVerifier_h + +#include "nsCOMPtr.h" +#include "nsIObserver.h" +#include "nsIStreamListener.h" +#include "nsNSSShutDown.h" +#include "nsString.h" +#include "nsTArray.h" +#include "ScopedNSSTypes.h" + +/** + * Mediator intercepting OnStartRequest in nsHttpChannel, blocks until all + * data is read from the input stream, verifies the content signature and + * releases the request to the next listener if the verification is successful. + * If the verification fails or anything else goes wrong, a + * NS_ERROR_INVALID_SIGNATURE is thrown. + */ +class ContentVerifier : public nsIStreamListener + , public nsNSSShutDownObject +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIREQUESTOBSERVER + + explicit ContentVerifier(nsIStreamListener* aMediatedListener, + nsISupports* aMediatedContext) + : mNextListener(aMediatedListener) + , mContext(aMediatedContext) + , mCx(nullptr) {} + + nsresult Init(const nsAString& aContentSignatureHeader); + + // nsNSSShutDownObject + virtual void virtualDestroyNSSReference() override + { + destructorSafeDestroyNSSReference(); + } + +protected: + virtual ~ContentVerifier() + { + nsNSSShutDownPreventionLock locker; + if (isAlreadyShutDown()) { + return; + } + destructorSafeDestroyNSSReference(); + shutdown(calledFromObject); + } + + void destructorSafeDestroyNSSReference() + { + mCx = nullptr; + } + +private: + nsresult ParseContentSignatureHeader(const nsAString& aContentSignatureHeader); + nsresult GetVerificationKey(const nsAString& aKeyId); + + // utility function to parse input before put into verification functions + nsresult ParseInput(mozilla::ScopedSECKEYPublicKey& aPublicKeyOut, + mozilla::ScopedSECItem& aSignatureItemOut, + SECOidTag& aOidOut, + const nsNSSShutDownPreventionLock&); + + // create a verifier context and store it in mCx + nsresult CreateContext(); + + // Adds data to the context that was used to generate the signature. + nsresult Update(const nsACString& aData); + + // Finalises the signature and returns the result of the signature + // verification. + nsresult End(bool* _retval); + + // content and next listener for nsIStreamListener + nsCOMPtr mNextListener; + nsCOMPtr mContext; + + // verifier context for incrementel verifications + mozilla::UniqueVFYContext mCx; + // buffered content to verify + FallibleTArray mContent; + // signature to verify + nsCString mSignature; + // verification key + nsCString mKey; + // verification key preference + nsString mVks; +}; + +#endif /* mozilla_dom_ContentVerifier_h */ diff --git a/dom/security/moz.build b/dom/security/moz.build index 4c3ee164e79b..80e554815b77 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -7,6 +7,7 @@ TEST_DIRS += ['test'] EXPORTS.mozilla.dom += [ + 'ContentVerifier.h', 'nsContentSecurityManager.h', 'nsCSPContext.h', 'nsCSPService.h', @@ -21,6 +22,7 @@ EXPORTS += [ ] UNIFIED_SOURCES += [ + 'ContentVerifier.cpp', 'nsContentSecurityManager.cpp', 'nsCSPContext.cpp', 'nsCSPParser.cpp', diff --git a/security/manager/ssl/ScopedNSSTypes.h b/security/manager/ssl/ScopedNSSTypes.h index a3376b15967c..d1316b724936 100644 --- a/security/manager/ssl/ScopedNSSTypes.h +++ b/security/manager/ssl/ScopedNSSTypes.h @@ -294,6 +294,11 @@ inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * e return SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); } +inline void VFY_DestroyContext_true(VFYContext * ctx) +{ + VFY_DestroyContext(ctx, true); +} + } // namespace internal // Deprecated: use the equivalent UniquePtr templates instead. @@ -361,6 +366,9 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey, MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule, SECMODModule, SECMOD_DestroyModule) +MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext, + VFYContext, + internal::VFY_DestroyContext_true) } // namespace mozilla #endif // mozilla_ScopedNSSTypes_h diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build index 083442a5ed3f..3cadc19f0575 100644 --- a/security/manager/ssl/moz.build +++ b/security/manager/ssl/moz.build @@ -61,6 +61,7 @@ EXPORTS += [ 'nsNSSHelper.h', 'nsNSSShutDown.h', 'nsRandomGenerator.h', + 'nsSecurityHeaderParser.h', 'NSSErrorsService.h', 'ScopedNSSTypes.h', 'SharedCertVerifier.h', diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index d82f398efd11..c01b455cad51 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -179,6 +179,10 @@ * a document with a calculated checksum that does not match the Content-MD5 * http header. */ ERROR(NS_ERROR_CORRUPTED_CONTENT, FAILURE(29)), + /* A content signature verification failed for some reason. This can be either + * an actual verification error, or any other error that led to the fact that + * a content signature that was expected couldn't be verified. */ + ERROR(NS_ERROR_INVALID_SIGNATURE, FAILURE(58)), /* While parsing for the first component of a header field using syntax as in * Content-Disposition or Content-Type, the first component was found to be * empty, such as in: Content-Disposition: ; filename=foo */ From 0d2149893d462676f1d5b0f52ff0d5e15a08f133 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:56:52 +0100 Subject: [PATCH 18/24] Bug 1226928 - network and ipc bits for content-signing on remote about:newtab, r=mayhemer --- ipc/glue/BackgroundUtils.cpp | 2 + netwerk/base/LoadInfo.cpp | 21 ++++++++ netwerk/base/LoadInfo.h | 2 + netwerk/base/nsILoadInfo.idl | 11 ++++ netwerk/ipc/NeckoChannelParams.ipdlh | 1 + netwerk/protocol/http/nsHttpChannel.cpp | 68 +++++++++++++++++++++++++ netwerk/protocol/http/nsHttpChannel.h | 11 ++++ 7 files changed, 116 insertions(+) diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 07149753a407..816f9f02c759 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -244,6 +244,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo, aLoadInfo->InternalContentPolicyType(), static_cast(aLoadInfo->GetTainting()), aLoadInfo->GetUpgradeInsecureRequests(), + aLoadInfo->GetVerifySignedContent(), aLoadInfo->GetInnerWindowID(), aLoadInfo->GetOuterWindowID(), aLoadInfo->GetParentOuterWindowID(), @@ -303,6 +304,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs, loadInfoArgs.contentPolicyType(), static_cast(loadInfoArgs.tainting()), loadInfoArgs.upgradeInsecureRequests(), + loadInfoArgs.verifySignedContent(), loadInfoArgs.innerWindowID(), loadInfoArgs.outerWindowID(), loadInfoArgs.parentOuterWindowID(), diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index 08ec22e34175..37545349c482 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -46,6 +46,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mInternalContentPolicyType(aContentPolicyType) , mTainting(LoadTainting::Basic) , mUpgradeInsecureRequests(false) + , mVerifySignedContent(false) , mInnerWindowID(0) , mOuterWindowID(0) , mParentOuterWindowID(0) @@ -118,6 +119,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow, , mInternalContentPolicyType(nsIContentPolicy::TYPE_DOCUMENT) , mTainting(LoadTainting::Basic) , mUpgradeInsecureRequests(false) + , mVerifySignedContent(false) , mInnerWindowID(0) , mOuterWindowID(0) , mParentOuterWindowID(0) @@ -155,6 +157,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) , mInternalContentPolicyType(rhs.mInternalContentPolicyType) , mTainting(rhs.mTainting) , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests) + , mVerifySignedContent(rhs.mVerifySignedContent) , mInnerWindowID(rhs.mInnerWindowID) , mOuterWindowID(rhs.mOuterWindowID) , mParentOuterWindowID(rhs.mParentOuterWindowID) @@ -177,6 +180,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, nsContentPolicyType aContentPolicyType, LoadTainting aTainting, bool aUpgradeInsecureRequests, + bool aVerifySignedContent, uint64_t aInnerWindowID, uint64_t aOuterWindowID, uint64_t aParentOuterWindowID, @@ -195,6 +199,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal, , mInternalContentPolicyType(aContentPolicyType) , mTainting(aTainting) , mUpgradeInsecureRequests(aUpgradeInsecureRequests) + , mVerifySignedContent(aVerifySignedContent) , mInnerWindowID(aInnerWindowID) , mOuterWindowID(aOuterWindowID) , mParentOuterWindowID(aParentOuterWindowID) @@ -427,6 +432,22 @@ LoadInfo::GetUpgradeInsecureRequests(bool* aResult) return NS_OK; } +NS_IMETHODIMP +LoadInfo::SetVerifySignedContent(bool aVerifySignedContent) +{ + MOZ_ASSERT(mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT, + "can only verify content for TYPE_DOCUMENT"); + mVerifySignedContent = aVerifySignedContent; + return NS_OK; +} + +NS_IMETHODIMP +LoadInfo::GetVerifySignedContent(bool* aResult) +{ + *aResult = mVerifySignedContent; + return NS_OK; +} + NS_IMETHODIMP LoadInfo::GetInnerWindowID(uint64_t* aResult) { diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h index 5894a9929574..955d7e448b30 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -80,6 +80,7 @@ private: nsContentPolicyType aContentPolicyType, LoadTainting aTainting, bool aUpgradeInsecureRequests, + bool aVerifySignedContent, uint64_t aInnerWindowID, uint64_t aOuterWindowID, uint64_t aParentOuterWindowID, @@ -117,6 +118,7 @@ private: nsContentPolicyType mInternalContentPolicyType; LoadTainting mTainting; bool mUpgradeInsecureRequests; + bool mVerifySignedContent; uint64_t mInnerWindowID; uint64_t mOuterWindowID; uint64_t mParentOuterWindowID; diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index 6360d367a853..c9c6f16702b7 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -336,6 +336,17 @@ interface nsILoadInfo : nsISupports */ [infallible] readonly attribute boolean upgradeInsecureRequests; + /** + * If true, the content of the channel is queued up and checked + * if it matches a content signature. Note, setting this flag + * to true will negatively impact performance since the preloader + * can not start until all of the content is fetched from the + * netwerk. + * + * Only use that in combination with TYPE_DOCUMENT. + */ + [infallible] attribute boolean verifySignedContent; + /** * Typically these are the window IDs of the window in which the element being * loaded lives. However, if the element being loaded is GetVerifySignedContent), but not present, or + // present but not valid, fail this channel and return + // NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a + // fallback load in nsDocShell. + if (!mCanceled) { + rv = ProcessContentSignatureHeader(mResponseHead); + if (NS_FAILED(rv)) { + LOG(("Content-signature verification failed.\n")); + return rv; + } + } + LOG((" calling mListener->OnStartRequest\n")); if (mListener) { MOZ_ASSERT(!mOnStartRequestCalled, @@ -1325,6 +1340,51 @@ nsHttpChannel::ProcessSecurityHeaders() return NS_OK; } +nsresult +nsHttpChannel::ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead) +{ + nsresult rv = NS_OK; + + // we only do this if we require it in loadInfo + if (!mLoadInfo || !mLoadInfo->GetVerifySignedContent()) { + return NS_OK; + } + NS_ENSURE_TRUE(aResponseHead, NS_ERROR_ABORT); + nsAutoCString contentSignatureHeader; + nsHttpAtom atom = nsHttp::ResolveAtom("Content-Signature"); + rv = aResponseHead->GetHeader(atom, contentSignatureHeader); + if (NS_FAILED(rv)) { + LOG(("Content-Signature header is missing but expected.")); + DoInvalidateCacheEntry(mURI); + return NS_ERROR_INVALID_SIGNATURE; + } + + // if we require a signature but it is empty, fail + if (contentSignatureHeader.IsEmpty()) { + DoInvalidateCacheEntry(mURI); + LOG(("An expected content-signature header is missing.\n")); + return NS_ERROR_INVALID_SIGNATURE; + } + + // we ensure a content type here to avoid running into problems with + // content sniffing, which might sniff parts of the content before we can + // verify the signature + if (aResponseHead->ContentType().IsEmpty()) { + NS_WARNING("Empty content type can get us in trouble when verifying " + "content signatures"); + return NS_ERROR_INVALID_SIGNATURE; + } + // create a new listener that meadiates the content + RefPtr contentVerifyingMediator = + new ContentVerifier(mListener, mListenerContext); + rv = contentVerifyingMediator->Init( + NS_ConvertUTF8toUTF16(contentSignatureHeader)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE); + mListener = contentVerifyingMediator; + + return NS_OK; +} + /** * Decide whether or not to send a security report and, if so, give the * SecurityReporter the information required to send such a report. @@ -3355,6 +3415,14 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v")); } + + // If a content signature is expected to be valid in this load, + // set doValidation to force a signature check. + if (!doValidation && + mLoadInfo && mLoadInfo->GetVerifySignedContent()) { + doValidation = true; + } + if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) && (methodWasGet || methodWasHead)) { const char *requestedETag, *cachedETag; diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 8c3334eb23ad..13e421ddc488 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -366,6 +366,17 @@ private: */ nsresult ProcessSecurityHeaders(); + /** + * Taking care of the Content-Signature header and fail the channel if + * the signature verification fails or is required but the header is not + * present. + * This sets mListener to ContentVerifier, which buffers the entire response + * before verifying the Content-Signature header. If the verification is + * successful, the load proceeds as usual. If the verification fails, a + * NS_ERROR_INVALID_SIGNATURE is thrown and a fallback loaded in nsDocShell + */ + nsresult ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead); + /** * A function that will, if the feature is enabled, send security reports. */ From ad505434379f4b6be7dfe6a7695ee9aaf8009abc Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:57:03 +0100 Subject: [PATCH 19/24] Bug 1226928 - dochsell changes for content-signing on remote about:newtab, r=bz,mconley --- browser/components/about/AboutRedirector.cpp | 18 +++++-- .../components/newtab/aboutNewTabService.js | 31 ++++++++--- docshell/base/nsDocShell.cpp | 51 ++++++++++++++++++- docshell/base/nsDocShell.h | 3 ++ 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/browser/components/about/AboutRedirector.cpp b/browser/components/about/AboutRedirector.cpp index 3396d56c7bda..7cd94a812473 100644 --- a/browser/components/about/AboutRedirector.cpp +++ b/browser/components/about/AboutRedirector.cpp @@ -155,6 +155,7 @@ AboutRedirector::NewChannel(nsIURI* aURI, for (int i = 0; i < kRedirTotal; i++) { if (!strcmp(path.get(), kRedirMap[i].id)) { nsAutoCString url; + nsLoadFlags loadFlags = static_cast(nsIChannel::LOAD_NORMAL); if (path.EqualsLiteral("newtab")) { // let the aboutNewTabService decide where to redirect @@ -163,6 +164,17 @@ AboutRedirector::NewChannel(nsIURI* aURI, NS_ENSURE_SUCCESS(rv, rv); rv = aboutNewTabService->GetDefaultURL(url); NS_ENSURE_SUCCESS(rv, rv); + + // if about:newtab points to an external resource we have to make sure + // the content is signed and trusted + bool remoteEnabled = false; + rv = aboutNewTabService->GetRemoteEnabled(&remoteEnabled); + NS_ENSURE_SUCCESS(rv, rv); + if (remoteEnabled) { + NS_ENSURE_ARG_POINTER(aLoadInfo); + aLoadInfo->SetVerifySignedContent(true); + loadFlags = static_cast(nsIChannel::LOAD_REPLACE); + } } // fall back to the specified url in the map if (url.IsEmpty()) { @@ -183,9 +195,9 @@ AboutRedirector::NewChannel(nsIURI* aURI, &isUIResource); NS_ENSURE_SUCCESS(rv, rv); - nsLoadFlags loadFlags = - isUIResource ? static_cast(nsIChannel::LOAD_NORMAL) - : static_cast(nsIChannel::LOAD_REPLACE); + loadFlags = isUIResource + ? static_cast(nsIChannel::LOAD_NORMAL) + : static_cast(nsIChannel::LOAD_REPLACE); rv = NS_NewChannelInternal(getter_AddRefs(tempChannel), tempURI, diff --git a/browser/components/newtab/aboutNewTabService.js b/browser/components/newtab/aboutNewTabService.js index 1aaa71756291..c46b53bc7914 100644 --- a/browser/components/newtab/aboutNewTabService.js +++ b/browser/components/newtab/aboutNewTabService.js @@ -32,6 +32,9 @@ const ABOUT_URL = "about:newtab"; // Pref that tells if remote newtab is enabled const PREF_REMOTE_ENABLED = "browser.newtabpage.remote"; +// Pref branch necesssary for testing +const PREF_REMOTE_CS_TEST = "browser.newtabpage.remote.content-signing-test"; + // The preference that tells whether to match the OS locale const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; @@ -126,8 +129,13 @@ AboutNewTabService.prototype = { return false; } + let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST); if (stateEnabled) { - this._remoteURL = this.generateRemoteURL(); + if (!csTest) { + this._remoteURL = this.generateRemoteURL(); + } else { + this._remoteURL = this._newTabURL; + } NewTabPrefsProvider.prefs.on( PREF_SELECTED_LOCALE, this._updateRemoteMaybe); @@ -144,7 +152,9 @@ AboutNewTabService.prototype = { NewTabPrefsProvider.prefs.off(PREF_REMOTE_MODE, this._updateRemoteMaybe); this._remoteEnabled = false; } - this._newTabURL = ABOUT_URL; + if (!csTest) { + this._newTabURL = ABOUT_URL; + } return true; }, @@ -170,10 +180,13 @@ AboutNewTabService.prototype = { * This URL only depends on the browser.newtabpage.remote pref. Overriding * the newtab page has no effect on the result of this function. * + * The result is also the remote URL if this is in a test (PREF_REMOTE_CS_TEST) + * * @returns {String} the default newtab URL, remote or local depending on browser.newtabpage.remote */ get defaultURL() { - if (this._remoteEnabled) { + let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST); + if (this._remoteEnabled || csTest) { return this._remoteURL; } return LOCAL_NEWTAB_URL; @@ -219,6 +232,7 @@ AboutNewTabService.prototype = { }, set newTabURL(aNewTabURL) { + let csTest = Services.prefs.getBoolPref(PREF_REMOTE_CS_TEST); aNewTabURL = aNewTabURL.trim(); if (aNewTabURL === ABOUT_URL) { // avoid infinite redirects in case one sets the URL to about:newtab @@ -233,14 +247,19 @@ AboutNewTabService.prototype = { let isResetRemote = prefRemoteEnabled && aNewTabURL === remoteURL; if (isResetLocal || isResetRemote) { - if (this._overriden) { - // only trigger a reset if previously overridden + if (this._overriden && !csTest) { + // only trigger a reset if previously overridden and this is no test this.resetNewTabURL(); } return; } // turn off remote state if needed - this.toggleRemote(false); + if (!csTest) { + this.toggleRemote(false); + } else { + // if this is a test, we want the remoteURL to be set + this._remoteURL = aNewTabURL; + } this._newTabURL = aNewTabURL; this._overridden = true; Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index f47073ad9ea1..513a7d625133 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7579,6 +7579,17 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress, aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) { DisplayLoadError(aStatus, url, nullptr, aChannel); return NS_OK; + } else if (aStatus == NS_ERROR_INVALID_SIGNATURE) { + // NS_ERROR_INVALID_SIGNATURE indicates a content-signature error. + // This currently only happens in case a remote about page fails. + // We have to load a fallback in this case. + // XXX: We always load about blank here, firefox has to overwrite this if + // it wants to display something else. + return LoadURI(MOZ_UTF16("about:blank"), // URI string + nsIChannel::LOAD_NORMAL, // Load flags + nullptr, // Referring URI + nullptr, // Post data stream + nullptr); // Headers stream } // Handle iframe document not loading error because source was @@ -9549,6 +9560,27 @@ nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer, return *aResult ? NS_OK : NS_ERROR_FAILURE; } +bool +nsDocShell::IsAboutNewtab(nsIURI* aURI) +{ + if (!aURI) { + return false; + } + bool isAbout; + if (NS_WARN_IF(NS_FAILED(aURI->SchemeIs("about", &isAbout)))) { + return false; + } + if (!isAbout) { + return false; + } + + nsAutoCString module; + if (NS_WARN_IF(NS_FAILED(NS_GetAboutModuleName(aURI, module)))) { + return false; + } + return module.Equals("newtab"); +} + NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aOriginalURI, @@ -10218,8 +10250,16 @@ nsDocShell::InternalLoad(nsIURI* aURI, // used to cancel attempts to load URIs in the wrong process. nsCOMPtr browserChrome3 = do_GetInterface(mTreeOwner); if (browserChrome3) { + // In case this is a remote newtab load, set aURI to aOriginalURI (newtab). + // This ensures that the verifySignedContent flag is set on loadInfo in + // DoURILoad. + nsIURI* uriForShouldLoadCheck = aURI; + if (IsAboutNewtab(aOriginalURI)) { + uriForShouldLoadCheck = aOriginalURI; + } bool shouldLoad; - rv = browserChrome3->ShouldLoadURI(this, aURI, aReferrer, &shouldLoad); + rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer, + &shouldLoad); if (NS_SUCCEEDED(rv) && !shouldLoad) { return NS_OK; } @@ -10869,6 +10909,15 @@ nsDocShell::DoURILoad(nsIURI* aURI, // Referrer is currenly only set for link clicks here. httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy); } + // set Content-Signature enforcing bit if aOriginalURI == about:newtab + if (aOriginalURI && httpChannel) { + if (IsAboutNewtab(aOriginalURI)) { + nsCOMPtr loadInfo = httpChannel->GetLoadInfo(); + if (loadInfo) { + loadInfo->SetVerifySignedContent(true); + } + } + } } nsCOMPtr scriptChannel = do_QueryInterface(channel); diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 9c040133351a..12af20ab0415 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -728,6 +728,9 @@ protected: nsIDocShellLoadInfo* aLoadInfo, bool aFirstParty); + // Check if aURI is about:newtab. + bool IsAboutNewtab(nsIURI* aURI); + protected: nsresult GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos); nsresult SetCurScrollPosEx(int32_t aCurHorizontalPos, From 2b22d469bbc857b68c8111b263139f5f18ffa1df Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 14 Mar 2016 11:57:16 +0100 Subject: [PATCH 20/24] Bug 1226928 - content-signature verification tests for about:newtab, r=mconley --- dom/security/test/contentverifier/browser.ini | 10 + .../browser_verify_content_about_newtab.js | 188 ++++++++++++++++++ .../contentverifier/file_about_newtab.html | 11 + .../file_about_newtab_bad.html | 11 + .../file_about_newtab_bad_signature | 1 + .../file_about_newtab_broken_signature | 1 + .../file_about_newtab_good_signature | 1 + .../contentverifier/file_contentserver.sjs | 179 +++++++++++++++++ .../test/contentverifier/signature.der | Bin 0 -> 103 bytes dom/security/test/contentverifier/sk.pem | 9 + dom/security/test/moz.build | 1 + 11 files changed, 412 insertions(+) create mode 100644 dom/security/test/contentverifier/browser.ini create mode 100644 dom/security/test/contentverifier/browser_verify_content_about_newtab.js create mode 100644 dom/security/test/contentverifier/file_about_newtab.html create mode 100644 dom/security/test/contentverifier/file_about_newtab_bad.html create mode 100644 dom/security/test/contentverifier/file_about_newtab_bad_signature create mode 100644 dom/security/test/contentverifier/file_about_newtab_broken_signature create mode 100644 dom/security/test/contentverifier/file_about_newtab_good_signature create mode 100644 dom/security/test/contentverifier/file_contentserver.sjs create mode 100644 dom/security/test/contentverifier/signature.der create mode 100644 dom/security/test/contentverifier/sk.pem diff --git a/dom/security/test/contentverifier/browser.ini b/dom/security/test/contentverifier/browser.ini new file mode 100644 index 000000000000..590855962289 --- /dev/null +++ b/dom/security/test/contentverifier/browser.ini @@ -0,0 +1,10 @@ +[DEFAULT] +support-files = + file_contentserver.sjs + file_about_newtab.html + file_about_newtab_bad.html + file_about_newtab_good_signature + file_about_newtab_bad_signature + file_about_newtab_broken_signature + +[browser_verify_content_about_newtab.js] diff --git a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js new file mode 100644 index 000000000000..07c07bdb4cd6 --- /dev/null +++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js @@ -0,0 +1,188 @@ +/* + * Test Content-Signature for remote about:newtab + * - Bug 1226928 - allow about:newtab to load remote content + * + * This tests content-signature verification on remote about:newtab in the + * following cases (see TESTS, all failed loads display about:blank fallback): + * - good case (signature should verify and correct page is displayed) + * - reload of newtab when the siganture was invalidated after the last correct + * load + * - malformed content-signature header + * - malformed keyid directive + * - malformed p384ecdsa directive + * - wrong signature (this is not a siganture for the delivered document) + * - invalid signature (this is not even a signature) + * - loading a file that doesn't fit the key or signature + * - cache poisoning (load a malicious remote page not in newtab, subsequent + * newtab load has to load the fallback) + */ + +const ABOUT_NEWTAB_URI = "about:newtab"; + +const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?"; +const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good"; + +const INVALIDATE_FILE = BASE + "invalidateFile=yep"; +const VALIDATE_FILE = BASE + "validateFile=yep"; + +const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header="; +const URI_ERROR_HEADER = URI_HEADER_BASE + "error"; +const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid"; +const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature"; +const URI_NO_HEADER = URI_HEADER_BASE + "noHeader"; + +const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good"; +const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good"; +const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good"; +const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good"; +const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad"; + +const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true"; + +const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928"; +const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928"; +const ABOUT_BLANK = ""; + +const TESTS = [ + // { newtab (aboutURI) or regular load (url) : url, + // testString : expected string in the loaded page } + { "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING }, + { "aboutURI" : URI_ERROR_HEADER, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_KEYERROR_HEADER, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_SIGERROR_HEADER, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_NO_HEADER, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_BAD_SIG, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_BROKEN_SIG, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_BAD_KEY, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_BAD_FILE, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_BAD_ALL, "testString" : ABOUT_BLANK }, + { "url" : URI_BAD_FILE_CACHED, "testString" : BAD_ABOUT_STRING }, + { "aboutURI" : URI_BAD_FILE_CACHED, "testString" : ABOUT_BLANK }, + { "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING } +]; + +var browser = null; +var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"] + .getService(Ci.nsIAboutNewTabService); + +function pushPrefs(...aPrefs) { + return new Promise((resolve) => { + SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve); + }); +} + +/* + * run tests with input from TESTS + */ +function doTest(aExpectedString, reload, aUrl, aNewTabPref) { + // set about:newtab location for this test if it's a newtab test + if (aNewTabPref) { + aboutNewTabService.newTabURL = aNewTabPref; + } + + // set prefs + yield pushPrefs( + ["browser.newtabpage.remote.content-signing-test", true], + ["browser.newtabpage.remote", true], [ + "browser.newtabpage.remote.keys", + "RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" + + "a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" + + "B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" + + "CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" + + "3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" + + "kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" + + "Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" + + "Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6" + ]); + + // start the test + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: aUrl, + }, + function * (browser) { + // check if everything's set correct for testing + ok(Services.prefs.getBoolPref( + "browser.newtabpage.remote.content-signing-test"), + "sanity check: remote newtab signing test should be used"); + ok(Services.prefs.getBoolPref("browser.newtabpage.remote"), + "sanity check: remote newtab should be used"); + // we only check this if we really do a newtab test + if (aNewTabPref) { + ok(aboutNewTabService.overridden, + "sanity check: default URL for about:newtab should be overriden"); + is(aboutNewTabService.newTabURL, aNewTabPref, + "sanity check: default URL for about:newtab should return the new URL"); + } + yield ContentTask.spawn( + browser, aExpectedString, function * (aExpectedString) { + ok(content.document.documentElement.innerHTML.includes(aExpectedString), + "Expect the following value in the result\n" + aExpectedString + + "\nand got " + content.document.documentElement.innerHTML); + }); + + // for good test cases we check if a reload fails if the remote page + // changed from valid to invalid in the meantime + if (reload) { + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: INVALIDATE_FILE, + }, + function * (browser2) { + yield ContentTask.spawn(browser2, null, function * () { + ok(content.document.documentElement.innerHTML.includes("Done"), + "Expect the following value in the result\n" + "Done" + + "\nand got " + content.document.documentElement.innerHTML); + }); + } + ); + + browser.reload(); + yield BrowserTestUtils.browserLoaded(browser); + + aExpectedString = ABOUT_BLANK; + yield ContentTask.spawn(browser, aExpectedString, + function * (aExpectedString) { + ok(content.document.documentElement.innerHTML.includes(aExpectedString), + "Expect the following value in the result\n" + aExpectedString + + "\nand got " + content.document.documentElement.innerHTML); + } + ); + + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: VALIDATE_FILE, + }, + function * (browser2) { + yield ContentTask.spawn(browser2, null, function * () { + ok(content.document.documentElement.innerHTML.includes("Done"), + "Expect the following value in the result\n" + "Done" + + "\nand got " + content.document.documentElement.innerHTML); + }); + } + ); + } + } + ); +} + +add_task(function * test() { + // run tests from TESTS + for (let i = 0; i < TESTS.length; i++) { + let testCase = TESTS[i]; + let url = "", aNewTabPref = ""; + let reload = false; + let aExpectedString = testCase.testString; + if (testCase.aboutURI) { + url = ABOUT_NEWTAB_URI; + aNewTabPref = testCase.aboutURI; + if (aExpectedString == GOOD_ABOUT_STRING) { + reload = true; + } + } else { + url = testCase.url; + } + + yield doTest(aExpectedString, reload, url, aNewTabPref); + } +}); diff --git a/dom/security/test/contentverifier/file_about_newtab.html b/dom/security/test/contentverifier/file_about_newtab.html new file mode 100644 index 000000000000..f274743b9149 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab.html @@ -0,0 +1,11 @@ + + + + + + Testpage for bug 1226928 + + + Just a fully good testpage for Bug 1226928
+ + \ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad.html b/dom/security/test/contentverifier/file_about_newtab_bad.html new file mode 100644 index 000000000000..45899f4f4fc0 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad.html @@ -0,0 +1,11 @@ + + + + + + Testpage for bug 1226928 + + + Just a bad testpage for Bug 1226928
+ + \ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_signature b/dom/security/test/contentverifier/file_about_newtab_bad_signature new file mode 100644 index 000000000000..73a3c1e34ecb --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad_signature @@ -0,0 +1 @@ +KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX \ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_broken_signature b/dom/security/test/contentverifier/file_about_newtab_broken_signature new file mode 100644 index 000000000000..468a167ffd39 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_broken_signature @@ -0,0 +1 @@ +MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg \ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_good_signature b/dom/security/test/contentverifier/file_about_newtab_good_signature new file mode 100644 index 000000000000..27c9adcaef89 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_good_signature @@ -0,0 +1 @@ +XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O \ No newline at end of file diff --git a/dom/security/test/contentverifier/file_contentserver.sjs b/dom/security/test/contentverifier/file_contentserver.sjs new file mode 100644 index 000000000000..656a5abff955 --- /dev/null +++ b/dom/security/test/contentverifier/file_contentserver.sjs @@ -0,0 +1,179 @@ +// sjs for remote about:newtab (bug 1226928) + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.importGlobalProperties(["URLSearchParams"]); + +const path = "browser/dom/security/test/contentverifier/"; + +const goodFileName = "file_about_newtab.html"; +const goodFileBase = path + goodFileName; +const goodFile = FileUtils.getDir("TmpD", [], true); +goodFile.append(goodFileName); +const goodSignature = path + "file_about_newtab_good_signature"; +const goodKeyId = "RemoteNewTab"; + +const badFile = path + "file_about_newtab_bad.html"; +const brokenSignature = path + "file_about_newtab_broken_signature"; +const badSignature = path + "file_about_newtab_bad_signature"; +const badKeyId = "OldRemoteNewTabKey"; + +// we copy the file to serve as newtab to a temp directory because +// we modify it during tests. +setupTestFile(); + +function setupTestFile() { + let tempFile = FileUtils.getDir("TmpD", [], true); + tempFile.append(goodFileName); + if (!tempFile.exists()) { + let fileIn = getFileName(goodFileBase, "CurWorkD"); + fileIn.copyTo(FileUtils.getDir("TmpD", [], true), ""); + } +} + +function getFileName(filePath, dir) { + // Since it's relative to the cwd of the test runner, we start there and + // append to get to the actual path of the file. + let testFile = + Cc["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get(dir, Components.interfaces.nsILocalFile); + let dirs = filePath.split("/"); + for (let i = 0; i < dirs.length; i++) { + testFile.append(dirs[i]); + } + return testFile; +} + +function loadFile(file) { + // Load a file to return it. + let testFileStream = + Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Components.interfaces.nsIFileInputStream); + testFileStream.init(file, -1, 0, 0); + return NetUtil.readInputStreamToString(testFileStream, + testFileStream.available()); +} + +function appendToFile(aFile, content) { + try { + let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_APPEND | + FileUtils.MODE_WRONLY); + file.write(content, content.length); + file.close(); + } catch (e) { + dump(">>> Error in appendToFile "+e); + return "Error"; + } + return "Done"; +} + +function truncateFile(aFile, length) { + let fileIn = loadFile(aFile); + fileIn = fileIn.slice(0, -length); + + try { + let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_WRONLY | + FileUtils.MODE_TRUNCATE); + file.write(fileIn, fileIn.length); + file.close(); + } catch (e) { + dump(">>> Error in truncateFile "+e); + return "Error"; + } + return "Done"; +} + +/* + * handle requests of the following form: + * sig=good&key=good&file=good&header=good&cached=no to serve pages with + * content signatures + * + * it further handles invalidateFile=yep and validateFile=yep to change the + * served file + */ +function handleRequest(request, response) { + let params = new URLSearchParams(request.queryString); + let keyType = params.get("key"); + let signatureType = params.get("sig"); + let fileType = params.get("file"); + let headerType = params.get("header"); + let cached = params.get("cached"); + let invalidateFile = params.get("invalidateFile"); + let validateFile = params.get("validateFile"); + + // if invalidateFile is set, this doesn't actually return a newtab page + // but changes the served file to invalidate the signature + // NOTE: make sure to make the file valid again afterwards! + if (invalidateFile) { + response.setHeader("Content-Type", "text/html", false); + let r = appendToFile(goodFile, "!"); + response.write(r); + return; + } + + // if validateFile is set, this doesn't actually return a newtab page + // but changes the served file to make the signature valid again + if (validateFile) { + response.setHeader("Content-Type", "text/html", false); + let r = truncateFile(goodFile, 1); + response.write(r); + return; + } + + // avoid confusing cache behaviours + if (!cached) { + response.setHeader("Cache-Control", "no-cache", false); + } else { + response.setHeader("Cache-Control", "max-age=3600", false); + } + + // send HTML to test allowed/blocked behaviours + response.setHeader("Content-Type", "text/html", false); + + // set signature header and key for Content-Signature header + /* By default a good content-signature header is returned. Any broken return + * value has to be indicated in the url. + */ + let csHeader = ""; + let keyId = goodKeyId; + let signature = goodSignature; + let file = goodFile; + if (keyType == "bad") { + keyId = badKeyId; + } + if (signatureType == "bad") { + signature = badSignature; + } else if (signatureType == "broken") { + signature = brokenSignature; + } + if (fileType == "bad") { + file = getFileName(badFile, "CurWorkD"); + } + + if (headerType == "good") { + // a valid content-signature header + csHeader = "keyid=" + keyId + ";p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "error") { + // this content-signature header is missing ; before p384ecdsa + csHeader = "keyid=" + keyId + "p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "errorInKeyid") { + // this content-signature header is missing the keyid directive + csHeader = "keid=" + keyId + ";p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "errorInSignature") { + // this content-signature header is missing the p384ecdsa directive + csHeader = "keyid=" + keyId + ";p385ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } + + if (csHeader) { + response.setHeader("Content-Signature", csHeader, false); + } + let result = loadFile(file); + + response.write(result); +} diff --git a/dom/security/test/contentverifier/signature.der b/dom/security/test/contentverifier/signature.der new file mode 100644 index 0000000000000000000000000000000000000000..011b94142c3d79406bbd812a154ade1531a5a002 GIT binary patch literal 103 zcmV-t0GR(UWdbl<60>?e;^{C2WzE%Q%Yu4I{^8aZBoR3Iz0Nw)t2tkZBQh@na3P%K zb!tDP5^XyIF#wVug>oV>SLNi{1rU`KGaT;dTd_Eqc?>E>+X2hV&>?Fg)Xr`f;VN)v JqyasuT~785E71S| literal 0 HcmV?d00001 diff --git a/dom/security/test/contentverifier/sk.pem b/dom/security/test/contentverifier/sk.pem new file mode 100644 index 000000000000..2ed514b9f6ec --- /dev/null +++ b/dom/security/test/contentverifier/sk.pem @@ -0,0 +1,9 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAzX2TrGOr0WE92AbAl+nqnpqh25pKCLYNMTV2hJHztrkVPWOp8w0mh +scIodK8RMpagBwYFK4EEACKhZANiAATiTcWYbt0Wg63dO7OXvpptNG0ryxv+v+Js +JJ5Upr3pFus5fZyKxzP9NPzB+oFhL/xw3jMx7X5/vBGaQ2sJSiNlHVkqZgzYF6JQ +4yUyiqTY7v67CyfUPA1BJg/nxOS9m3o= +-----END EC PRIVATE KEY----- diff --git a/dom/security/test/moz.build b/dom/security/test/moz.build index ad11c90fbad8..dc7ca098976c 100644 --- a/dom/security/test/moz.build +++ b/dom/security/test/moz.build @@ -24,5 +24,6 @@ MOCHITEST_CHROME_MANIFESTS += [ ] BROWSER_CHROME_MANIFESTS += [ + 'contentverifier/browser.ini', 'csp/browser.ini', ] From 4cc4528de3f667fd7e696ab83ea8c646d64bdce3 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Mon, 14 Mar 2016 07:08:48 -0400 Subject: [PATCH 21/24] Bug 1255766 - Tracelogger: Mark resizing of memory also as internal tracelogger time, r=bbouvier --- js/src/vm/TraceLogging.cpp | 53 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index 6078bd25444e..9a498be32e5b 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -552,14 +552,37 @@ TraceLoggerThread::log(uint32_t id) return; MOZ_ASSERT(traceLoggerState); - if (!events.ensureSpaceBeforeAdd()) { + if (!events.hasSpaceForAdd()) { uint64_t start = rdtsc() - traceLoggerState->startupTime; - if (graph.get()) - graph->log(events); + if (!events.ensureSpaceBeforeAdd()) { + if (graph.get()) + graph->log(events); - iteration_++; - events.clear(); + iteration_++; + events.clear(); + + // Remove the item in the pointerMap for which the payloads + // have no uses anymore + for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) { + if (e.front().value()->uses() != 0) + continue; + + TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId()); + MOZ_ASSERT(p); + textIdPayloads.remove(p); + + e.removeFront(); + } + + // Free all payloads that have no uses anymore. + for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) { + if (e.front().value()->uses() == 0) { + js_delete(e.front().value()); + e.removeFront(); + } + } + } // Log the time it took to flush the events as being from the // Tracelogger. @@ -574,26 +597,6 @@ TraceLoggerThread::log(uint32_t id) entryStop.textId = TraceLogger_Stop; } - // Remove the item in the pointerMap for which the payloads - // have no uses anymore - for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) { - if (e.front().value()->uses() != 0) - continue; - - TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId()); - MOZ_ASSERT(p); - textIdPayloads.remove(p); - - e.removeFront(); - } - - // Free all payloads that have no uses anymore. - for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) { - if (e.front().value()->uses() == 0) { - js_delete(e.front().value()); - e.removeFront(); - } - } } uint64_t time = rdtsc() - traceLoggerState->startupTime; From dac18564ebf7f6736a75b0ab67e138b38255abe7 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Mon, 14 Mar 2016 07:08:50 -0400 Subject: [PATCH 22/24] Bug 1255316 - IonMonkey: Enable folding of MLoadUnboxedObjectOrNull with the stored value, r=jandem --- js/src/jit/MIR.cpp | 23 +++++++++++++++++++++++ js/src/jit/MIR.h | 1 + 2 files changed, 24 insertions(+) diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index c93de6d4119b..4e28d4f01dd3 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4724,6 +4724,29 @@ MLoadElement::foldsTo(TempAllocator& alloc) return foldsToStoredValue(alloc, store->value()); } +MDefinition* +MLoadUnboxedObjectOrNull::foldsTo(TempAllocator& alloc) +{ + if (!dependency() || !dependency()->isStoreUnboxedObjectOrNull()) + return this; + + MStoreUnboxedObjectOrNull* store = dependency()->toStoreUnboxedObjectOrNull(); + if (!store->block()->dominates(block())) + return this; + + if (store->elements() != elements()) + return this; + + if (store->index() != index()) + return this; + + if (store->value()->type() == MIRType_ObjectOrNull) + return this; + + MOZ_ASSERT(offsetAdjustment() == store->offsetAdjustment()); + return foldsToStoredValue(alloc, store->value()); +} + // Gets the MDefinition* representing the source/target object's storage. // Usually this is just an MElements*, but sometimes there are layers // of indirection or inlining, which are handled elsewhere. diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index a703febc7a48..23e00ee08ac6 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9298,6 +9298,7 @@ class MLoadUnboxedObjectOrNull AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::UnboxedElement); } + MDefinition* foldsTo(TempAllocator& alloc) override; bool mightAlias(const MDefinition* store) const override; ALLOW_CLONE(MLoadUnboxedObjectOrNull) From 9267d1e64fcb6424b0aa2cf9e0162a528bb74c23 Mon Sep 17 00:00:00 2001 From: James Graham Date: Tue, 8 Mar 2016 16:24:00 +0000 Subject: [PATCH 23/24] Bug 1215812 - Increase the internal timeout for a test that depends on a window opening, r=Ms2ger MozReview-Commit-ID: HzNYyHiehbU --HG-- extra : rebase_source : c255aac4b598a21d136c88c76bed9888fbc2158b --- .../browsing-context-names/002.html.ini | 9 ------- .../windows/browsing-context-names/002.html | 27 +++++++++---------- 2 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini diff --git a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini deleted file mode 100644 index 656c2a8829a2..000000000000 --- a/testing/web-platform/meta/html/browsers/windows/browsing-context-names/002.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[002.html] - type: testharness - disabled: - if debug or (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1215461 - [Link with target=_blank, no rel] - expected: - if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL - if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL - diff --git a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html index e4ef84189009..4a1df8e4d039 100644 --- a/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html +++ b/testing/web-platform/tests/html/browsers/windows/browsing-context-names/002.html @@ -5,22 +5,21 @@
Link From 49b4d6e62e62b537890b293d1daa62c8ec2209a0 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 14 Mar 2016 14:22:51 +0100 Subject: [PATCH 24/24] Bug 1256291: Fix unified build under js/; r=Ms2ger MozReview-Commit-ID: Amia14nqztf --HG-- extra : rebase_source : 74b6b7dcfe7ccc408ad6be1e0cf87a42e5f9c98c --- js/src/asmjs/WasmBinaryToText.cpp | 1 + js/src/asmjs/WasmTextToBinary.cpp | 1 + js/src/gc/Marking.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/js/src/asmjs/WasmBinaryToText.cpp b/js/src/asmjs/WasmBinaryToText.cpp index 680275852276..57351583da20 100644 --- a/js/src/asmjs/WasmBinaryToText.cpp +++ b/js/src/asmjs/WasmBinaryToText.cpp @@ -21,6 +21,7 @@ #include "mozilla/CheckedInt.h" #include "jsnum.h" +#include "jsprf.h" #include "asmjs/Wasm.h" #include "asmjs/WasmTypes.h" diff --git a/js/src/asmjs/WasmTextToBinary.cpp b/js/src/asmjs/WasmTextToBinary.cpp index 6bd86ffecfbc..3583ed938080 100644 --- a/js/src/asmjs/WasmTextToBinary.cpp +++ b/js/src/asmjs/WasmTextToBinary.cpp @@ -28,6 +28,7 @@ #include "jsstr.h" #include "asmjs/WasmBinary.h" +#include "asmjs/WasmTypes.h" #include "ds/LifoAlloc.h" #include "js/CharacterEncoding.h" #include "js/HashTable.h" diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index e405006b0404..ecbe2beb71bd 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -15,6 +15,7 @@ #include "jsgc.h" #include "jsprf.h" +#include "asmjs/WasmModule.h" #include "builtin/ModuleObject.h" #include "gc/GCInternals.h" #include "gc/Policy.h"