Merge m-c to fx-team, a=merge

MozReview-Commit-ID: COpA4LGoMQN
This commit is contained in:
Wes Kocher 2016-03-14 17:40:44 -07:00
commit 8350d308c3
121 changed files with 4628 additions and 726 deletions

View File

@ -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);

View File

@ -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<nsLoadFlags>(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<nsLoadFlags>(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<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
loadFlags = isUIResource
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,

View File

@ -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);

View File

@ -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<nsIWebBrowserChrome3> 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<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
if (loadInfo) {
loadInfo->SetVerifySignedContent(true);
}
}
}
}
nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);

View File

@ -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,

View File

@ -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 =

View File

@ -21,9 +21,9 @@ struct AnimationPerformanceWarning
TransformBackfaceVisibilityHidden,
TransformPreserve3D,
TransformSVG,
TransformWithGeometricProperties,
TransformFrameInactive,
OpacityFrameInactive,
WithGeometricProperties
};
explicit AnimationPerformanceWarning(Type aType)

View File

@ -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<ComputedTimingFunction>
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()) {

View File

@ -19,10 +19,6 @@ namespace mozilla {
class ComputedTimingFunction;
namespace dom {
class Element;
}
class AnimationUtils
{
public:
@ -59,7 +55,7 @@ public:
* be returned.
*/
static Maybe<ComputedTimingFunction>
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.

View File

@ -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;
}

View File

@ -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"
@ -652,14 +653,27 @@ KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
{
}
template <class KeyframeEffectType>
template <class KeyframeEffectType, class OptionsType>
/* static */ already_AddRefed<KeyframeEffectType>
KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv)
KeyframeEffectReadOnly::ConstructKeyframeEffect(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
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, doc, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (aTarget.IsNull()) {
// We don't support null targets yet.
aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
@ -694,29 +708,11 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
RefPtr<KeyframeEffectType> effect =
new KeyframeEffectType(targetElement->OwnerDoc(), targetElement,
pseudoType, aTiming);
pseudoType, timingParams);
effect->mProperties = Move(animationProperties);
return effect.forget();
}
// Explicit instantiations to avoid linker errors.
template
already_AddRefed<KeyframeEffectReadOnly>
KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
template
already_AddRefed<KeyframeEffect>
KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv);
void
KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
{
@ -1287,10 +1283,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<ComputedTimingFunction> 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 +1549,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<ComputedTimingFunction> 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.
@ -1757,6 +1750,19 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList(
}
}
/* static */ already_AddRefed<KeyframeEffectReadOnly>
KeyframeEffectReadOnly::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffectReadOnly>(aGlobal, aTarget,
aFrames, aOptions,
aRv);
}
void
KeyframeEffectReadOnly::GetTarget(
Nullable<OwningElementOrCSSPseudoElement>& aRv) const
@ -2135,7 +2141,7 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
}
bool
KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(
KeyframeEffectReadOnly::ShouldBlockAsyncTransformAnimations(
const nsIFrame* aFrame,
AnimationPerformanceWarning::Type& aPerformanceWarning) const
{
@ -2155,7 +2161,7 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(
// Check for geometric properties
if (IsGeometricProperty(property.mProperty)) {
aPerformanceWarning =
AnimationPerformanceWarning::Type::WithGeometricProperties;
AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
return true;
}
@ -2215,6 +2221,30 @@ KeyframeEffect::WrapObject(JSContext* aCx,
return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<KeyframeEffect>
KeyframeEffect::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
aOptions, aRv);
}
/* static */ already_AddRefed<KeyframeEffect>
KeyframeEffect::Constructor(
const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
aOptions, aRv);
}
void KeyframeEffect::NotifySpecifiedTimingUpdated()
{
nsIDocument* doc = nullptr;

View File

@ -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;
@ -202,16 +203,7 @@ public:
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv)
{
TimingParams timingParams =
TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
if (aRv.Failed()) {
return nullptr;
}
return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
aGlobal, aTarget, aFrames, timingParams, aRv);
}
ErrorResult& aRv);
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
void GetFrames(JSContext*& aCx,
@ -314,24 +306,16 @@ public:
void GetPropertyState(nsTArray<AnimationPropertyState>& 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;
@ -354,12 +338,12 @@ protected:
virtual ~KeyframeEffectReadOnly();
template<typename KeyframeEffectType>
template<class KeyframeEffectType, class OptionsType>
static already_AddRefed<KeyframeEffectType>
ConstructKeyframeEffect(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
const OptionsType& aOptions,
ErrorResult& aRv);
void ResetIsRunningOnCompositor();
@ -433,29 +417,17 @@ public:
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
ErrorResult& aRv)
{
TimingParams timingParams =
TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
if (aRv.Failed()) {
return nullptr;
}
return ConstructKeyframeEffect<KeyframeEffect>(
aGlobal, aTarget, aFrames, timingParams, aRv);
}
ErrorResult& aRv);
// More generalized version for Animatable.animate.
// Variant of Constructor that accepts a KeyframeAnimationOptions object
// for use with for Animatable.animate.
// Not exposed to content.
static already_AddRefed<KeyframeEffect>
inline Constructor(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const TimingParams& aTiming,
ErrorResult& aRv)
{
return ConstructKeyframeEffect<KeyframeEffect>(aGlobal, aTarget, aFrames,
aTiming, aRv);
}
Constructor(const GlobalObject& aGlobal,
const Nullable<ElementOrCSSPseudoElement>& aTarget,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aRv);
void NotifySpecifiedTimingUpdated();

View File

@ -6,6 +6,8 @@
#include "mozilla/TimingParams.h"
#include "nsIDocument.h"
namespace mozilla {
template <class OptionsType>
@ -32,10 +34,9 @@ GetTimingProperties(
template <class OptionsType>
static TimingParams
TimingParamsFromOptionsUnion(
const OptionsType& aOptions,
const Nullable<dom::ElementOrCSSPseudoElement>& 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<dom::Element> 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(targetElement, timing.mEasing);
result.mFunction = AnimationUtils::ParseEasing(timing.mEasing, aDocument);
}
return result;
}
@ -91,19 +76,19 @@ TimingParamsFromOptionsUnion(
/* static */ TimingParams
TimingParams::FromOptionsUnion(
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
const Nullable<dom::ElementOrCSSPseudoElement>& 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<dom::ElementOrCSSPseudoElement>& aTarget,
nsIDocument* aDocument,
ErrorResult& aRv)
{
return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv);
}
bool

View File

@ -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<dom::ElementOrCSSPseudoElement>& aTarget,
ErrorResult& aRv);
nsIDocument* aDocument, ErrorResult& aRv);
static TimingParams FromOptionsUnion(
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
ErrorResult& aRv);
nsIDocument* aDocument, ErrorResult& aRv);
// Range-checks and validates an UnrestrictedDoubleOrString or
// OwningUnrestrictedDoubleOrString object and converts to a

View File

@ -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() {
@ -229,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);
@ -252,6 +536,112 @@ function start() {
}, subtest.desc);
});
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) {
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(

View File

@ -3353,14 +3353,8 @@ Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
}
}
TimingParams timingParams =
TimingParams::FromOptionsUnion(aOptions, aTarget, aError);
if (aError.Failed()) {
return nullptr;
}
RefPtr<KeyframeEffect> effect =
KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError);
KeyframeEffect::Constructor(global, aTarget, frames, aOptions, aError);
if (aError.Failed()) {
return nullptr;
}

View File

@ -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")
@ -923,6 +924,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")

View File

@ -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<BluetoothHidReportType>(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<BluetoothGattWriteType, int32_t>(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<uint8_t>(aIn.mDescriptorValue, aIn.mDescriptorLength), aPDU);
}
nsresult
PackPDU(const BluetoothHidReport& aIn, DaemonSocketPDU& aPDU)
{
uint8_t* reportData =
const_cast<uint8_t*>(aIn.mReportData.Elements());
nsresult rv = PackPDU(
PackConversion<uint32_t, uint16_t>(aIn.mReportData.Length()), aPDU);
if (NS_FAILED(rv)) {
return rv;
}
return PackPDU(
PackArray<uint8_t>(reportData, aIn.mReportData.Length()),
aPDU);
}
nsresult
PackPDU(BluetoothHidProtocolMode aIn, DaemonSocketPDU& aPDU)
{
return PackPDU(
PackConversion<BluetoothHidProtocolMode, uint8_t>(aIn), aPDU);
}
nsresult
PackPDU(BluetoothHidReportType aIn, DaemonSocketPDU& aPDU)
{
return PackPDU(
PackConversion<BluetoothHidReportType, uint8_t>(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<uint8_t>(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<uint8_t, BluetoothHidProtocolMode>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidConnectionState& aOut)
{
return UnpackPDU(
aPDU, UnpackConversion<uint8_t, BluetoothHidConnectionState>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHidStatus& aOut)
{
return UnpackPDU(
aPDU, UnpackConversion<uint8_t, BluetoothHidStatus>(aOut));
}
END_BLUETOOTH_NAMESPACE

View File

@ -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)
*/

View File

@ -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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> 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<DaemonSocketPDU> pdu(
new DaemonSocketPDU(SERVICE_ID, OPCODE_SEND_DATA, 0));
nsresult rv = PackPDU(aRemoteAddr, aDataLen,
PackArray<uint8_t>(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<BluetoothHidResultHandler> res =
static_cast<BluetoothHidResultHandler*>(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<BluetoothHidResultHandler, void,
BluetoothStatus, BluetoothStatus>::Dispatch(
aRes, &BluetoothHidResultHandler::OnError,
ConstantInitOp1<BluetoothStatus>(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

View File

@ -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

View File

@ -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<RefPtr<DaemonSocketResultHandler>> 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

View File

@ -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<BluetoothDaemonA2dpInterface> mA2dpInterface;
nsAutoPtr<BluetoothDaemonAvrcpInterface> mAvrcpInterface;
nsAutoPtr<BluetoothDaemonGattInterface> mGattInterface;
nsAutoPtr<BluetoothDaemonHidInterface> mHidInterface;
};
END_BLUETOOTH_NAMESPACE

View File

@ -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<BluetoothHidManager> 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<BluetoothProfileResultHandler> 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<BluetoothProfileResultHandler> mRes;
nsresult mRv;
};
// static
void
BluetoothHidManager::InitHidInterface(BluetoothProfileResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
if (sBluetoothHidInterface) {
BT_LOGR("Bluetooth HID interface is already initialized.");
RefPtr<nsRunnable> 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<nsRunnable> 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<nsRunnable> 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<nsRunnable> 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<nsIObserverService> 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<BluetoothProfileResultHandler> 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<BluetoothProfileResultHandler> 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<nsRunnable> 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<nsRunnable> 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<nsRunnable> 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<nsIObserverService> 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)

View File

@ -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<BluetoothProfileController> mController;
};
END_BLUETOOTH_NAMESPACE
#endif // mozilla_dom_bluetooth_bluedroid_BluetoothHidManager_h

View File

@ -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");

View File

@ -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<uint8_t> 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

View File

@ -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
//

View File

@ -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();

View File

@ -188,6 +188,7 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
AddProfile(BluetoothHfpManager::Get());
AddProfile(BluetoothA2dpManager::Get());
AddProfile(BluetoothAvrcpManager::Get()); // register after A2DP
AddProfile(BluetoothHidManager::Get());
return;
}

View File

@ -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<BluetoothNamedValue>& arr =

View File

@ -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

View File

@ -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',

View File

@ -23,7 +23,6 @@ using namespace dom;
AsyncEventDispatcher::AsyncEventDispatcher(EventTarget* aTarget,
WidgetEvent& aEvent)
: mTarget(aTarget)
, mOnlyChromeDispatch(false)
{
MOZ_ASSERT(mTarget);
RefPtr<Event> event =
@ -37,6 +36,9 @@ AsyncEventDispatcher::AsyncEventDispatcher(EventTarget* aTarget,
NS_IMETHODIMP
AsyncEventDispatcher::Run()
{
if (mCanceled) {
return NS_OK;
}
RefPtr<Event> 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()
{

View File

@ -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<dom::EventTarget> mTarget;
nsCOMPtr<nsIDOMEvent> 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:

View File

@ -421,6 +421,10 @@ EVENT(timeupdate,
eTimeUpdate,
EventNameType_HTML,
eBasicEventClass)
EVENT(toggle,
eToggle,
EventNameType_HTML,
eBasicEventClass)
EVENT(volumechange,
eVolumeChange,
EventNameType_HTML,

View File

@ -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<JSObject*> aGivenProto)
{

View File

@ -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<JSObject*> 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<HTMLDetailsElement*>(mTarget.get());
details->mToggleEventDispatcher = nullptr;
return AsyncEventDispatcher::Run();
}
};
RefPtr<ToggleEventDispatcher> mToggleEventDispatcher;
};
} // namespace dom

View File

@ -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

View File

@ -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<nsCString>* decodedData =
static_cast<FallibleTArray<nsCString>*>(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<nsIInputStream> 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<nsSecurityHeaderDirective>* 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;
}

View File

@ -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<nsIStreamListener> mNextListener;
nsCOMPtr<nsISupports> mContext;
// verifier context for incrementel verifications
mozilla::UniqueVFYContext mCx;
// buffered content to verify
FallibleTArray<nsCString> mContent;
// signature to verify
nsCString mSignature;
// verification key
nsCString mKey;
// verification key preference
nsString mVks;
};
#endif /* mozilla_dom_ContentVerifier_h */

View File

@ -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',

View File

@ -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]

View File

@ -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 = "<head></head><body></body>";
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);
}
});

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
<head>
<meta charset="utf-8">
<title>Testpage for bug 1226928</title>
</head>
<body>
Just a fully good testpage for Bug 1226928<br/>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
<head>
<meta charset="utf-8">
<title>Testpage for bug 1226928</title>
</head>
<body>
Just a bad testpage for Bug 1226928<br/>
</body>
</html>

View File

@ -0,0 +1 @@
KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX

View File

@ -0,0 +1 @@
MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg

View File

@ -0,0 +1 @@
XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O

View File

@ -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);
}

Binary file not shown.

View File

@ -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-----

View File

@ -24,5 +24,6 @@ MOCHITEST_CHROME_MANIFESTS += [
]
BROWSER_CHROME_MANIFESTS += [
'contentverifier/browser.ini',
'csp/browser.ini',
]

View File

@ -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;

View File

@ -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;

View File

@ -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<nsIURI> 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)

View File

@ -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
/* */

View File

@ -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
* <xsl:import> and document()

View File

@ -1,4 +0,0 @@
<?xml-stylesheet type="text/xsl" href="file_bug1222624.xsl"?>
<root>
<load>file_bug1222624_data2.xml</load>
</root>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="file_bug1222624_sub.xsl"/>
<xsl:template match="/root">
<result>
<xsl:call-template name="external"/>
<xsl:value-of select="document('file_bug1222624_data1.xml')"/>
<xsl:text>!</xsl:text>
<xsl:value-of select="document(load)"/>
</result>
</xsl:template>
</xsl:stylesheet>

View File

@ -1 +0,0 @@
<data>doc1</data>

View File

@ -1 +0,0 @@
<data>doc2</data>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:include href="file_bug1222624_sub_sub.xsl"/>
</xsl:stylesheet>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="external">
<external/>
</xsl:template>
</xsl:stylesheet>

View File

@ -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]

View File

@ -1,53 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1222624
-->
<head>
<title>Test for Bug 1222624</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1222624">Mozilla Bug 1222624</a>
<p id="display"></p>
<iframe id="frame"></iframe>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 1222624 **/
const nl = (navigator.platform == 'Win32' ||
navigator.platform == 'Win64') ? '\r\n' : '\n';
const transformRes = '<?xml version="1.0" encoding="UTF-8"?>' + nl + '<result><external/>doc1!doc2</result>';
xhr = new XMLHttpRequest();
xhr.open("GET", "file_bug1222624.xml", false);
xhr.send();
var xmlDoc = xhr.responseXML;
xhr.open("GET", "file_bug1222624.xsl", false);
xhr.send();
var xslDoc = xhr.responseXML;
var processor = new XSLTProcessor;
processor.importStylesheet(xslDoc);
var apiRes = new XMLSerializer().serializeToString(processor.transformToDocument(xmlDoc));
is(apiRes, transformRes, "API transformation correct");
SimpleTest.waitForExplicitFinish();
var frame = $("frame");
frame.src = "file_bug1222624.xml";
frame.onload = function () {
var piRes = new XMLSerializer().serializeToString(frame.contentDocument);
is(piRes, transformRes, "processing-instruction transformation correct");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

View File

@ -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<nsIURI> documentURI;
nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader);
nsCOMPtr<nsILoadGroup> 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);

View File

@ -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

View File

@ -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 */

View File

@ -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,

View File

@ -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<nsIURI> 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<nsIURI> 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);

View File

@ -21,28 +21,27 @@ txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
{
mSourceDocument = aSourceDocument;
nsCOMPtr<nsIURI> 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<nsIURI> 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

View File

@ -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<nsURIHashKey, txLoadedDocumentInfo>
class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
{
public:
txLoadedDocumentsHash()
: nsClassHashtable<nsURIHashKey, txLoadedDocumentInfo>(4),
: nsTHashtable<txLoadedDocumentEntry>(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<txIGlobalParameter>* 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<txIGlobalParameter>* mGlobalParams;
nsCOMPtr<nsIDocument> mLoadingDocument;
txLoadedDocumentsHash mLoadedDocuments;
txKeyHash mKeyHash;
RefPtr<txResultRecycler> mRecycler;

View File

@ -257,16 +257,6 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
nsCOMPtr<nsIPrincipal> channelPrincipal;
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
channel, getter_AddRefs(channelPrincipal));
mCompiler->setPrincipal(channelPrincipal);
nsCOMPtr<nsIURI> 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<txMozillaXSLTProcessor> 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<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> referrerUri;
rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> 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<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
if (!loadGroup) {
return NS_ERROR_FAILURE;
@ -468,8 +473,7 @@ txCompileObserver::startLoad(nsIURI* aUri, nsIPrincipal* aReferrerPrincipal,
nsCOMPtr<nsIURI> 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<txStylesheetSink> 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<txCompileObserver> observer =
new txCompileObserver(aProcessor, aLoaderDocument);
nsAutoCString fragment;
aUri->GetRef(fragment);
NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
RefPtr<txStylesheetCompiler> 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<txMozillaXSLTProcessor> mProcessor;
nsCOMPtr<nsIDocument> 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<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> referrerUri;
rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> 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<nsINode> source;
if (mProcessor) {
source =
do_QueryInterface(mProcessor->GetSourceContentModel());
}
nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr);
nsCOMPtr<nsIDOMDocument> document;
nsCOMPtr<nsILoadGroup> 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<nsIDocument> doc = do_QueryInterface(document);
nsCOMPtr<nsIURI> 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<nsIDocument> doc = aNode->OwnerDoc();
nsCOMPtr<nsIURI> uri;
if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
uri = static_cast<nsIContent*>(aNode)->GetBaseURI();
}
else {
NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc");
uri = static_cast<nsIDocument*>(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<txSyncCompileObserver> obs =
new txSyncCompileObserver(aProcessor, aLoaderDocument);
new txSyncCompileObserver(aProcessor);
NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY);
RefPtr<txStylesheetCompiler> compiler =
new txStylesheetCompiler(EmptyString(), obs);
new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs);
NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIURI> baseURI = aNode->GetBaseURI();
compiler->setBaseURI(baseURI);
compiler->setPrincipal(aNode->NodePrincipal());
nsresult rv = handleNode(aNode, compiler);
if (NS_FAILED(rv)) {

View File

@ -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<nsPIDOMWindowInner> win = do_QueryInterface(mOwner);
if (win) {
return win->GetExtantDoc();
}
}
if (mSource) {
nsCOMPtr<nsINode> 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

View File

@ -166,8 +166,6 @@ private:
void notifyError();
nsresult ensureStylesheet();
nsIDocument* getLoaderDoc();
nsCOMPtr<nsISupports> mOwner;
RefPtr<txStylesheet> 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);

View File

@ -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<nsIURI> 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<nsIURI> 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);

View File

@ -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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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<txInstruction>&& 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<nsIURI> 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<txToplevelItem> 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<txStylesheetCompiler*>(this);
nsAutoCString fragment;
aURI->GetRef(fragment);
RefPtr<txStylesheetCompiler> 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<nsIURI> 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<txStylesheetCompiler*>(this);
nsAutoCString fragment;
aURI->GetRef(fragment);
RefPtr<txStylesheetCompiler> 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<txStylesheetCompiler> compiler =
new txStylesheetCompiler(EmptyString(), nullptr);
new txStylesheetCompiler(EmptyString(),
mozilla::net::RP_Default, nullptr);
NS_ENSURE_TRUE(compiler, false);
nsAutoPtr<FunctionCall> 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)
{

View File

@ -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<nsIURI> mBaseURI;
nsString mBaseURI;
RefPtr<txNamespaceMap> mMappings;
nsTArray<int32_t> 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<txInstruction>&& 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<nsIPrincipal> mStylesheetPrincipal;
RefPtr<txACompileObserver> mObserver;
nsTArray<txInScopeVariable*> mInScopeVariables;
nsTArray<txStylesheetCompiler*> 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<txInstruction**> 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,

View File

@ -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<nsIURI> mBaseURI;
nsString mBaseURI;
};
/*

View File

@ -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<DrawTargetCairo> target = new DrawTargetCairo();

View File

@ -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)

View File

@ -244,6 +244,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->InternalContentPolicyType(),
static_cast<uint32_t>(aLoadInfo->GetTainting()),
aLoadInfo->GetUpgradeInsecureRequests(),
aLoadInfo->GetVerifySignedContent(),
aLoadInfo->GetInnerWindowID(),
aLoadInfo->GetOuterWindowID(),
aLoadInfo->GetParentOuterWindowID(),
@ -303,6 +304,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.contentPolicyType(),
static_cast<LoadTainting>(loadInfoArgs.tainting()),
loadInfoArgs.upgradeInsecureRequests(),
loadInfoArgs.verifySignedContent(),
loadInfoArgs.innerWindowID(),
loadInfoArgs.outerWindowID(),
loadInfoArgs.parentOuterWindowID(),

View File

@ -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.

View File

@ -21,6 +21,7 @@
#include "mozilla/CheckedInt.h"
#include "jsnum.h"
#include "jsprf.h"
#include "asmjs/Wasm.h"
#include "asmjs/WasmTypes.h"
@ -158,6 +159,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 +884,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:

View File

@ -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:;

View File

@ -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:

View File

@ -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"
@ -209,6 +210,7 @@ enum class WasmAstExprKind
Return,
SetLocal,
Store,
Trap,
UnaryOperator,
};
@ -238,6 +240,13 @@ struct WasmAstNop : WasmAstExpr
{}
};
struct WasmAstTrap : WasmAstExpr
{
WasmAstTrap()
: WasmAstExpr(WasmAstExprKind::Trap)
{}
};
class WasmAstConst : public WasmAstExpr
{
const Val val_;
@ -796,6 +805,7 @@ class WasmToken
Store,
Table,
Text,
Trap,
Type,
UnaryOpcode,
ValueType
@ -1931,6 +1941,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 +2681,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 +3387,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<WasmAstBinaryOperator>());
@ -3703,6 +3718,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<WasmAstBinaryOperator>());
case WasmAstExprKind::Block:

View File

@ -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:

View File

@ -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
};

View File

@ -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"

View File

@ -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);

View File

@ -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<RecompileFn>(Recompile);

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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.

View File

@ -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
@ -9282,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)

Some files were not shown because too many files have changed in this diff Show More