mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Merge m-c to fx-team, a=merge
MozReview-Commit-ID: COpA4LGoMQN
This commit is contained in:
commit
8350d308c3
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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 =
|
||||
|
@ -21,9 +21,9 @@ struct AnimationPerformanceWarning
|
||||
TransformBackfaceVisibilityHidden,
|
||||
TransformPreserve3D,
|
||||
TransformSVG,
|
||||
TransformWithGeometricProperties,
|
||||
TransformFrameInactive,
|
||||
OpacityFrameInactive,
|
||||
WithGeometricProperties
|
||||
};
|
||||
|
||||
explicit AnimationPerformanceWarning(Type aType)
|
||||
|
@ -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()) {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
*/
|
||||
|
677
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
Normal file
677
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.cpp
Normal 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
|
298
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h
Normal file
298
dom/bluetooth/bluedroid/BluetoothDaemonHidInterface.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
610
dom/bluetooth/bluedroid/BluetoothHidManager.cpp
Normal file
610
dom/bluetooth/bluedroid/BluetoothHidManager.cpp
Normal 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)
|
71
dom/bluetooth/bluedroid/BluetoothHidManager.h
Normal file
71
dom/bluetooth/bluedroid/BluetoothHidManager.h
Normal 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
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -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();
|
||||
|
@ -188,6 +188,7 @@ BluetoothProfileController::SetupProfiles(bool aAssignServiceClass)
|
||||
AddProfile(BluetoothHfpManager::Get());
|
||||
AddProfile(BluetoothA2dpManager::Get());
|
||||
AddProfile(BluetoothAvrcpManager::Get()); // register after A2DP
|
||||
AddProfile(BluetoothHidManager::Get());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -421,6 +421,10 @@ EVENT(timeupdate,
|
||||
eTimeUpdate,
|
||||
EventNameType_HTML,
|
||||
eBasicEventClass)
|
||||
EVENT(toggle,
|
||||
eToggle,
|
||||
EventNameType_HTML,
|
||||
eBasicEventClass)
|
||||
EVENT(volumechange,
|
||||
eVolumeChange,
|
||||
EventNameType_HTML,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
375
dom/security/ContentVerifier.cpp
Normal file
375
dom/security/ContentVerifier.cpp
Normal 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;
|
||||
}
|
98
dom/security/ContentVerifier.h
Normal file
98
dom/security/ContentVerifier.h
Normal 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 */
|
@ -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',
|
||||
|
10
dom/security/test/contentverifier/browser.ini
Normal file
10
dom/security/test/contentverifier/browser.ini
Normal 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]
|
@ -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);
|
||||
}
|
||||
});
|
11
dom/security/test/contentverifier/file_about_newtab.html
Normal file
11
dom/security/test/contentverifier/file_about_newtab.html
Normal 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>
|
11
dom/security/test/contentverifier/file_about_newtab_bad.html
Normal file
11
dom/security/test/contentverifier/file_about_newtab_bad.html
Normal 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>
|
@ -0,0 +1 @@
|
||||
KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX
|
@ -0,0 +1 @@
|
||||
MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg
|
@ -0,0 +1 @@
|
||||
XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O
|
179
dom/security/test/contentverifier/file_contentserver.sjs
Normal file
179
dom/security/test/contentverifier/file_contentserver.sjs
Normal 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);
|
||||
}
|
BIN
dom/security/test/contentverifier/signature.der
Normal file
BIN
dom/security/test/contentverifier/signature.der
Normal file
Binary file not shown.
9
dom/security/test/contentverifier/sk.pem
Normal file
9
dom/security/test/contentverifier/sk.pem
Normal 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-----
|
@ -24,5 +24,6 @@ MOCHITEST_CHROME_MANIFESTS += [
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'contentverifier/browser.ini',
|
||||
'csp/browser.ini',
|
||||
]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
/* */
|
||||
|
@ -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()
|
||||
|
@ -1,4 +0,0 @@
|
||||
<?xml-stylesheet type="text/xsl" href="file_bug1222624.xsl"?>
|
||||
<root>
|
||||
<load>file_bug1222624_data2.xml</load>
|
||||
</root>
|
@ -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>
|
@ -1 +0,0 @@
|
||||
<data>doc1</data>
|
@ -1 +0,0 @@
|
||||
<data>doc2</data>
|
@ -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>
|
@ -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>
|
@ -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]
|
||||
|
@ -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>
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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:;
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user