diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-04-14 21:49:04 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-04-14 21:49:04 +0200 |
commit | 39dac57259cff8b61db0b22cb2ad0a8adb02692e (patch) | |
tree | 52a026cc8c22793eb17fd0f5e22adce1ae08a1dd /dom | |
parent | a1cce3b2b00bbd9f4983013ddd8934a7bccb9e99 (diff) | |
parent | c2d9ab62f3d097c9e0e00184cab1f546554f5eaa (diff) | |
download | UXP-39dac57259cff8b61db0b22cb2ad0a8adb02692e.tar UXP-39dac57259cff8b61db0b22cb2ad0a8adb02692e.tar.gz UXP-39dac57259cff8b61db0b22cb2ad0a8adb02692e.tar.lz UXP-39dac57259cff8b61db0b22cb2ad0a8adb02692e.tar.xz UXP-39dac57259cff8b61db0b22cb2ad0a8adb02692e.zip |
Merge branch 'redwood' into 28.9-platform
Diffstat (limited to 'dom')
440 files changed, 15850 insertions, 37687 deletions
diff --git a/dom/base/BlobSet.h b/dom/base/BlobSet.h index 3e22955dd..bf98cb023 100644 --- a/dom/base/BlobSet.h +++ b/dom/base/BlobSet.h @@ -8,6 +8,9 @@ #define mozilla_dom_BlobSet_h #include "mozilla/RefPtr.h" +#include "jsfriendapi.h" +#include "nsString.h" +#include "nsTArray.h" namespace mozilla { namespace dom { diff --git a/dom/base/CORSMode.h b/dom/base/CORSMode.h index 82f2f570d..577e78725 100644 --- a/dom/base/CORSMode.h +++ b/dom/base/CORSMode.h @@ -7,6 +7,8 @@ #ifndef CORSMode_h_ #define CORSMode_h_ +#include <stdint.h> + namespace mozilla { enum CORSMode : uint8_t { diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 3cde261f0..b25f0b76b 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -7,6 +7,7 @@ #include "mozilla/Base64.h" #include "mozilla/BasePrincipal.h" +#include "jsfriendapi.h" namespace mozilla { namespace dom { diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index 051217c84..58a67b4f2 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -10,6 +10,7 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/ChromeUtilsBinding.h" #include "mozilla/dom/ThreadSafeChromeUtilsBinding.h" +#include "mozilla/dom/UnionTypes.h" #include "mozilla/ErrorResult.h" namespace mozilla { diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 3f202d33b..f582d635f 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -7,8 +7,11 @@ #include "mozilla/dom/CustomElementRegistry.h" #include "mozilla/dom/CustomElementRegistryBinding.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/WebComponentsBinding.h" +#include "mozilla/dom/Promise.h" +#include "nsContentUtils.h" #include "nsIParserService.h" #include "jsapi.h" diff --git a/dom/base/DocGroup.cpp b/dom/base/DocGroup.cpp index 226879985..ba9189168 100644 --- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -6,6 +6,7 @@ #include "mozilla/StaticPtr.h" #include "mozilla/ClearOnShutdown.h" #include "nsIDocShell.h" +#include "nsNetCID.h" namespace mozilla { namespace dom { diff --git a/dom/base/DocGroup.h b/dom/base/DocGroup.h index f4f7ac8ad..6ecd284c1 100644 --- a/dom/base/DocGroup.h +++ b/dom/base/DocGroup.h @@ -7,6 +7,7 @@ #ifndef DocGroup_h #define DocGroup_h +#include "nsIDocument.h" #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsIPrincipal.h" diff --git a/dom/base/GroupedSHistory.h b/dom/base/GroupedSHistory.h index 8c88a0aaf..c5e75d639 100644 --- a/dom/base/GroupedSHistory.h +++ b/dom/base/GroupedSHistory.h @@ -7,6 +7,8 @@ #ifndef GroupedSHistory_h #define GroupedSHistory_h +#include "nsCOMArray.h" +#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* #include "nsIFrameLoader.h" #include "nsIGroupedSHistory.h" #include "nsIPartialSHistory.h" diff --git a/dom/base/ImageTracker.cpp b/dom/base/ImageTracker.cpp index 9fef059bc..d0c231981 100644 --- a/dom/base/ImageTracker.cpp +++ b/dom/base/ImageTracker.cpp @@ -8,6 +8,8 @@ * animating */ #include "ImageTracker.h" +#include "mozilla/Preferences.h" +#include "nsAppRunner.h" // for XRE_IsContentProcess namespace mozilla { namespace dom { diff --git a/dom/base/ImageTracker.h b/dom/base/ImageTracker.h index d33dc55f8..f16461192 100644 --- a/dom/base/ImageTracker.h +++ b/dom/base/ImageTracker.h @@ -10,6 +10,7 @@ #ifndef mozilla_dom_ImageTracker #define mozilla_dom_ImageTracker +#include "imgIRequest.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" diff --git a/dom/base/MutableBlobStorage.h b/dom/base/MutableBlobStorage.h index fad391b16..ed368e5e6 100644 --- a/dom/base/MutableBlobStorage.h +++ b/dom/base/MutableBlobStorage.h @@ -7,6 +7,9 @@ #ifndef mozilla_dom_MutableBlobStorage_h #define mozilla_dom_MutableBlobStorage_h +#include "nsCycleCollectionParticipant.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" #include "mozilla/RefPtr.h" #include "prio.h" diff --git a/dom/base/MutableBlobStreamListener.cpp b/dom/base/MutableBlobStreamListener.cpp index 6769ad3e6..afcc04d9f 100644 --- a/dom/base/MutableBlobStreamListener.cpp +++ b/dom/base/MutableBlobStreamListener.cpp @@ -5,6 +5,7 @@ #include "MutableBlobStreamListener.h" #include "MutableBlobStorage.h" +#include "nsIInputStream.h" namespace mozilla { namespace dom { diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index fdf151b6c..a544f23c1 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -37,10 +37,7 @@ #include "mozilla/dom/PowerManager.h" #include "mozilla/dom/WakeLock.h" #include "mozilla/dom/power/PowerManagerService.h" -#include "mozilla/dom/FlyWebPublishedServer.h" -#include "mozilla/dom/FlyWebService.h" #include "mozilla/dom/Permissions.h" -#include "mozilla/dom/Presentation.h" #include "mozilla/dom/ServiceWorkerContainer.h" #include "mozilla/dom/StorageManager.h" #include "mozilla/dom/TCPSocket.h" @@ -67,8 +64,6 @@ #include "nsIAppsService.h" #include "mozIApplication.h" #include "WidgetUtils.h" -#include "nsIPresentationService.h" - #include "mozilla/dom/MediaDevices.h" #include "MediaManager.h" @@ -218,7 +213,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator) #ifdef MOZ_EME NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager) #endif - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation) #ifdef MOZ_GAMEPAD NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest) #endif @@ -284,10 +278,6 @@ Navigator::Invalidate() mTimeManager = nullptr; } - if (mPresentation) { - mPresentation = nullptr; - } - mServiceWorkerContainer = nullptr; #ifdef MOZ_EME @@ -1364,41 +1354,6 @@ Navigator::GetBattery(ErrorResult& aRv) return mBatteryPromise; } -already_AddRefed<Promise> -Navigator::PublishServer(const nsAString& aName, - const FlyWebPublishOptions& aOptions, - ErrorResult& aRv) -{ - RefPtr<FlyWebService> service = FlyWebService::GetOrCreate(); - if (!service) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<FlyWebPublishPromise> mozPromise = - service->PublishServer(aName, aOptions, mWindow); - MOZ_ASSERT(mozPromise); - - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow); - ErrorResult result; - RefPtr<Promise> domPromise = Promise::Create(global, result); - if (result.Failed()) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - mozPromise->Then(AbstractThread::MainThread(), - __func__, - [domPromise] (FlyWebPublishedServer* aServer) { - domPromise->MaybeResolve(aServer); - }, - [domPromise] (nsresult aStatus) { - domPromise->MaybeReject(aStatus); - }); - - return domPromise.forget(); -} - PowerManager* Navigator::GetMozPower(ErrorResult& aRv) { @@ -1931,19 +1886,5 @@ Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem, } #endif -Presentation* -Navigator::GetPresentation(ErrorResult& aRv) -{ - if (!mPresentation) { - if (!mWindow) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - mPresentation = Presentation::Create(mWindow); - } - - return mPresentation; -} - } // namespace dom } // namespace mozilla diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 91b7fc15c..4ddaaabab 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -39,8 +39,6 @@ class WakeLock; class ArrayBufferViewOrBlobOrStringOrFormData; class ServiceWorkerContainer; class DOMRequest; -struct FlyWebPublishOptions; -struct FlyWebFilter; } // namespace dom } // namespace mozilla @@ -74,7 +72,6 @@ class Connection; } // namespace network class PowerManager; -class Presentation; class LegacyMozTCPSocket; class StorageManager; @@ -141,9 +138,6 @@ public: Geolocation* GetGeolocation(ErrorResult& aRv); Promise* GetBattery(ErrorResult& aRv); - already_AddRefed<Promise> PublishServer(const nsAString& aName, - const FlyWebPublishOptions& aOptions, - ErrorResult& aRv); static void AppName(nsAString& aAppName, bool aUsePrefOverriddenValue); static nsresult GetPlatform(nsAString& aPlatform, @@ -210,8 +204,6 @@ public: system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv); #endif // MOZ_AUDIO_CHANNEL_MANAGER - Presentation* GetPresentation(ErrorResult& aRv); - bool SendBeacon(const nsAString& aUrl, const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData, ErrorResult& aRv); @@ -288,7 +280,6 @@ private: RefPtr<time::TimeManager> mTimeManager; RefPtr<ServiceWorkerContainer> mServiceWorkerContainer; nsCOMPtr<nsPIDOMWindowInner> mWindow; - RefPtr<Presentation> mPresentation; #ifdef MOZ_GAMEPAD RefPtr<GamepadServiceTest> mGamepadServiceTest; #endif diff --git a/dom/base/NodeInfo.cpp b/dom/base/NodeInfo.cpp index d32a00fd3..f055dfc75 100644 --- a/dom/base/NodeInfo.cpp +++ b/dom/base/NodeInfo.cpp @@ -15,6 +15,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Likely.h" +#include "mozilla/Unused.h" #include "nsNodeInfoManager.h" #include "nsCOMPtr.h" diff --git a/dom/base/Pose.h b/dom/base/Pose.h index c7ef1b381..0817c8c96 100644 --- a/dom/base/Pose.h +++ b/dom/base/Pose.h @@ -8,6 +8,7 @@ #define mozilla_dom_Pose_h #include "nsWrapperCache.h" +#include "mozilla/ErrorResult.h" namespace mozilla { namespace dom { diff --git a/dom/base/TabGroup.h b/dom/base/TabGroup.h index f9fa9eec5..c436ce98d 100644 --- a/dom/base/TabGroup.h +++ b/dom/base/TabGroup.h @@ -7,10 +7,12 @@ #ifndef TabGroup_h #define TabGroup_h +#include "nsIDocument.h" #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsIPrincipal.h" #include "nsTHashtable.h" +#include "nsHashKeys.h" #include "nsString.h" #include "mozilla/RefPtr.h" diff --git a/dom/base/ThirdPartyUtil.cpp b/dom/base/ThirdPartyUtil.cpp index 61d97f634..37d326dbc 100644 --- a/dom/base/ThirdPartyUtil.cpp +++ b/dom/base/ThirdPartyUtil.cpp @@ -10,6 +10,7 @@ #include "nsIChannel.h" #include "nsIServiceManager.h" #include "nsIHttpChannelInternal.h" +#include "nsPIDOMWindow.h" #include "nsIDOMWindow.h" #include "nsILoadContext.h" #include "nsIPrincipal.h" diff --git a/dom/base/TimeoutHandler.cpp b/dom/base/TimeoutHandler.cpp index 78c3f16dd..f34275840 100644 --- a/dom/base/TimeoutHandler.cpp +++ b/dom/base/TimeoutHandler.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TimeoutHandler.h" +#include "nsJSUtils.h" namespace mozilla { namespace dom { diff --git a/dom/base/TimeoutHandler.h b/dom/base/TimeoutHandler.h index cb0a0ce94..a80dc2995 100644 --- a/dom/base/TimeoutHandler.h +++ b/dom/base/TimeoutHandler.h @@ -7,9 +7,11 @@ #ifndef mozilla_dom_timeout_handler_h #define mozilla_dom_timeout_handler_h +#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* #include "nsCOMPtr.h" #include "nsISupports.h" #include "nsITimeoutHandler.h" +#include "nsString.h" namespace mozilla { namespace dom { diff --git a/dom/base/TimerClamping.h b/dom/base/TimerClamping.h index 2ffd6add5..2bd1f019c 100755 --- a/dom/base/TimerClamping.h +++ b/dom/base/TimerClamping.h @@ -7,6 +7,8 @@ #ifndef TimerClamping_h___ #define TimerClamping_h___ +#include <math.h> + namespace mozilla { class TimerClamping diff --git a/dom/base/moz.build b/dom/base/moz.build index 75ddefded..89f1785ca 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -225,7 +225,7 @@ EXPORTS.mozilla.dom += [ 'WindowOrientationObserver.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'AnonymousContent.cpp', 'Attr.cpp', 'BarProps.cpp', @@ -376,7 +376,7 @@ UNIFIED_SOURCES += [ ] if CONFIG['MOZ_WEBRTC']: - UNIFIED_SOURCES += [ + SOURCES += [ 'nsDOMDataChannel.cpp', ] diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index ebddcb7ed..2418fb501 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -1526,6 +1526,40 @@ nsAttrValue::ParseIntWithFallback(const nsAString& aString, int32_t aDefault, SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr); } +void +nsAttrValue::ParseClampedNonNegativeInt(const nsAString& aString, + int32_t aDefault, int32_t aMin, + int32_t aMax) +{ + ResetIfSet(); + + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result); + bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput); + + if (result & nsContentUtils::eParseHTMLInteger_ErrorOverflow) { + if (result & nsContentUtils::eParseHTMLInteger_Negative) { + val = aDefault; + } else { + val = aMax; + } + nonStrict = true; + } else if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 0) { + val = aDefault; + nonStrict = true; + } else if (val < aMin) { + val = aMin; + nonStrict = true; + } else if (val > aMax) { + val = aMax; + nonStrict = true; + } + + SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr); +} + bool nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString) { diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h index 655e4ca61..23f61a614 100644 --- a/dom/base/nsAttrValue.h +++ b/dom/base/nsAttrValue.h @@ -362,6 +362,19 @@ public: */ bool ParseNonNegativeIntValue(const nsAString& aString); + /** + * Parse a string value into a clamped non-negative integer. + * This method follows the rules for parsing non-negative integer from: + * https://html.spec.whatwg.org/multipage/infrastructure.html#clamped-to-the-range + * + * @param aString the string to parse + * @param aDefault value to return for negative or invalid values + * @param aMin minimum value + * @param aMax maximum value + */ + void ParseClampedNonNegativeInt(const nsAString& aString, int32_t aDefault, + int32_t aMin, int32_t aMax); + /** * Parse a string value into a positive integer. * This method follows the rules for parsing non-negative integer from: diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 800f40fa1..6a9904bf9 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -1080,6 +1080,7 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue, int sign = 1; if (*iter == char16_t('-')) { sign = -1; + result |= eParseHTMLInteger_Negative; ++iter; } else if (*iter == char16_t('+')) { result |= eParseHTMLInteger_NonStandard; diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 606d67de9..ffbd15e78 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -444,7 +444,9 @@ public: // Set if one or more error flags were set. eParseHTMLInteger_Error = 1 << 3, eParseHTMLInteger_ErrorNoValue = 1 << 4, - eParseHTMLInteger_ErrorOverflow = 1 << 5 + eParseHTMLInteger_ErrorOverflow = 1 << 5, + // Use this flag to detect the difference between overflow and underflow + eParseHTMLInteger_Negative = 1 << 6, }; static int32_t ParseHTMLInteger(const nsAString& aValue, ParseHTMLIntegerResultFlags *aResult); diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 858a30ce5..0273b7b94 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -7,6 +7,7 @@ #include "nsDOMMutationObserver.h" #include "mozilla/AnimationTarget.h" +#include "mozilla/CycleCollectedJSContext.h" #include "mozilla/Maybe.h" #include "mozilla/OwningNonNull.h" @@ -29,6 +30,9 @@ using mozilla::dom::TreeOrderComparator; using mozilla::dom::Animation; using mozilla::dom::Element; +using namespace mozilla; +using namespace mozilla::dom; + AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* nsDOMMutationObserver::sScheduledMutationObservers = nullptr; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index afe88a454..380593737 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -166,7 +166,6 @@ #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/MediaSource.h" -#include "mozilla/dom/FlyWebService.h" #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" @@ -8369,13 +8368,6 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest) return false; } - // Don't save presentation if there are active FlyWeb connections or FlyWeb - // servers. - FlyWebService* flyWebService = FlyWebService::GetExisting(); - if (flyWebService && flyWebService->HasConnectionOrServer(win->WindowID())) { - return false; - } - if (mSubDocuments) { for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) { auto entry = static_cast<SubDocMapEntry*>(iter.Get()); diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index fb350fa12..01c1944be 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1261,6 +1261,15 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags, isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow); } + // Exit fullscreen if a website focuses another window + if (!isElementInActiveWindow && aFlags & FLAG_RAISE) { + if (nsIDocument* doc = mActiveWindow ? mActiveWindow->GetDoc() : nullptr) { + if (doc && doc->GetFullscreenElement()) { + nsIDocument::AsyncExitFullscreen(doc); + } + } + } + // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX // system. We don't control event dispatch to windowed plugins on non-MacOSX, // so we can't display the "Press ESC to leave fullscreen mode" warning on diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index dfd380fc2..efea3ee40 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -57,6 +57,7 @@ #include "mozilla/dom/ErrorEvent.h" #include "nsAXPCNativeCallContext.h" #include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/Telemetry.h" #include "nsJSPrincipals.h" diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 98b367b66..c9cec96db 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -31,6 +31,7 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/ScriptSettings.h" +using namespace mozilla; using namespace mozilla::dom; bool diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 3ac00142d..25482fe7b 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -1144,7 +1144,7 @@ nsScriptLoader::InstantiateModuleTree(nsModuleLoadRequest* aRequest) nsModuleScript* ms = aRequest->mModuleScript; MOZ_ASSERT(ms); - if (!ms->ModuleRecord()) { + if (!ms || !ms->ModuleRecord()) { return false; } diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index b00af2085..941e65eff 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -53,11 +53,6 @@ DOMInterfaces = { 'concrete': False }, -'AddonManagerPermissions': { - 'wrapperCache': False, - 'concrete': False -}, - 'AnimationEffectReadOnly': { 'concrete': False }, @@ -340,14 +335,6 @@ DOMInterfaces = { 'wrapperCache': False, }, -'FlyWebFetchEvent': { - 'headerFile': 'FlyWebServerEvents.h', -}, - -'FlyWebWebSocketEvent': { - 'headerFile': 'FlyWebServerEvents.h', -}, - 'FontFaceSet': { 'implicitJSContext': [ 'load' ], }, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 6b23e8225..e3cca63b6 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1109,6 +1109,12 @@ class CGHeaders(CGWrapper): # Now find all the things we'll need as arguments because we # need to wrap or unwrap them. bindingHeaders = set() + + # KeyframeAnimationOptions.webidl is doing something VERY screwy and + # Unified Building really sucks so directly include this + if prefix == "KeyframeAnimationOptionsBinding": + bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") + declareIncludes = set(declareIncludes) def addHeadersForType((t, dictionary)): diff --git a/dom/bindings/GenerateCSS2PropertiesWebIDL.py b/dom/bindings/GenerateCSS2PropertiesWebIDL.py index 58ec60c29..57634494f 100644 --- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py +++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py @@ -16,6 +16,7 @@ def generateLine(propName, extendedAttrs): return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs), propName) def generate(output, idlFilename, preprocessorHeader): + print(idlFilename) cpp = list(buildconfig.substs['CPP']) cpp += shellutil.split(buildconfig.substs['ACDEFINES']) cpp.append(preprocessorHeader) diff --git a/dom/bindings/WebIDLGlobalNameHash.cpp b/dom/bindings/WebIDLGlobalNameHash.cpp index 7477b52e7..981a1a395 100644 --- a/dom/bindings/WebIDLGlobalNameHash.cpp +++ b/dom/bindings/WebIDLGlobalNameHash.cpp @@ -4,8 +4,12 @@ * 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 "jswrapper.h" #include "WebIDLGlobalNameHash.h" #include "js/GCAPI.h" +#include "XrayWrapper.h" +#include "XPCWrapper.h" +#include "mozilla/dom/Selection.h" #include "mozilla/HashFunctions.h" #include "mozilla/Maybe.h" #include "mozilla/dom/DOMJSProxyHandler.h" diff --git a/dom/bindings/WebIDLGlobalNameHash.h b/dom/bindings/WebIDLGlobalNameHash.h index bbe015395..cd7be2c69 100644 --- a/dom/bindings/WebIDLGlobalNameHash.h +++ b/dom/bindings/WebIDLGlobalNameHash.h @@ -8,6 +8,7 @@ #define mozilla_dom_WebIDLGlobalNameHash_h__ #include "js/RootingAPI.h" +#include "nsString.h" #include "nsTArray.h" namespace mozilla { diff --git a/dom/bindings/moz.build b/dom/bindings/moz.build index ed8a4d37e..ca82b48a8 100644 --- a/dom/bindings/moz.build +++ b/dom/bindings/moz.build @@ -89,7 +89,7 @@ LOCAL_INCLUDES += [ '/media/webrtc/signaling/src/peerconnection', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'BindingUtils.cpp', 'CallbackInterface.cpp', 'CallbackObject.cpp', diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index b6e351e52..69f3f5e25 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -269,6 +269,7 @@ class WebIDLCodegenManager(LoggingMixin): # Generate bindings from .webidl files. for filename in sorted(changed_inputs): basename = mozpath.basename(filename) + print basename result.inputs.add(filename) written, deps = self._generate_build_files_for_webidl(filename) result.created |= written[0] diff --git a/dom/bindings/nsScriptError.h b/dom/bindings/nsScriptError.h index b8049d0a0..ac8099c1c 100644 --- a/dom/bindings/nsScriptError.h +++ b/dom/bindings/nsScriptError.h @@ -14,8 +14,13 @@ #include "jsapi.h" #include "js/RootingAPI.h" +#include "jswrapper.h" +#include "nsCOMArray.h" +#include "nsContentUtils.h" +#include "nsCycleCollectionParticipant.h" #include "nsIScriptError.h" #include "nsString.h" +#include "nsStringFwd.h" class nsScriptErrorNote final : public nsIScriptErrorNote { public: diff --git a/dom/bindings/nsScriptErrorWithStack.cpp b/dom/bindings/nsScriptErrorWithStack.cpp index 74c00999f..0a8df38b3 100644 --- a/dom/bindings/nsScriptErrorWithStack.cpp +++ b/dom/bindings/nsScriptErrorWithStack.cpp @@ -22,7 +22,7 @@ using namespace mozilla::dom; namespace { static nsCString -FormatStackString(JSContext* cx, HandleObject aStack) { +FormatStackString(JSContext* cx, JS::HandleObject aStack) { JS::RootedString formattedStack(cx); if (!JS::BuildStackString(cx, aStack, &formattedStack)) { @@ -111,7 +111,7 @@ nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult) } JSContext* cx = jsapi.cx(); - RootedObject stack(cx, mStack); + JS::RootedObject stack(cx, mStack); nsCString stackString = FormatStackString(cx, stack); nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString; aResult.Assign(combined); diff --git a/dom/events/test/test_all_synthetic_events.html b/dom/events/test/test_all_synthetic_events.html index 90dbe95ee..58560fdce 100644 --- a/dom/events/test/test_all_synthetic_events.html +++ b/dom/events/test/test_all_synthetic_events.html @@ -139,10 +139,6 @@ const kEventConstructors = { return new ErrorEvent(aName, aProps); }, }, - FlyWebFetchEvent: { create: null, // Cannot create untrusted event from JS. - }, - FlyWebWebSocketEvent: { create: null, // Cannot create untrusted event from JS. - }, FocusEvent: { create: function (aName, aProps) { return new FocusEvent(aName, aProps); }, diff --git a/dom/flyweb/FlyWebDiscoveryManager.cpp b/dom/flyweb/FlyWebDiscoveryManager.cpp deleted file mode 100644 index 5a97eb6d8..000000000 --- a/dom/flyweb/FlyWebDiscoveryManager.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* -*- 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 "nsString.h" -#include "nsTHashtable.h" -#include "nsClassHashtable.h" -#include "nsIUUIDGenerator.h" -#include "jsapi.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/Logging.h" -#include "nsComponentManagerUtils.h" -#include "nsServiceManagerUtils.h" - -#include "mozilla/dom/FlyWebDiscoveryManager.h" -#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h" - -namespace mozilla { -namespace dom { - -static LazyLogModule gFlyWebDiscoveryManagerLog("FlyWebDiscoveryManager"); -#undef LOG_I -#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebDiscoveryManagerLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#undef LOG_E -#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebDiscoveryManagerLog, mozilla::LogLevel::Error, (__VA_ARGS__)) - - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(FlyWebDiscoveryManager) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(FlyWebDiscoveryManager) -NS_IMPL_CYCLE_COLLECTING_RELEASE(FlyWebDiscoveryManager) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FlyWebDiscoveryManager) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -FlyWebDiscoveryManager::FlyWebDiscoveryManager(nsISupports* aParent, - FlyWebService* aService) - : mParent(aParent) - , mService(aService) - , mNextId(0) -{ -} - -FlyWebDiscoveryManager::~FlyWebDiscoveryManager() -{ - mService->UnregisterDiscoveryManager(this); -} - -JSObject* -FlyWebDiscoveryManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return FlyWebDiscoveryManagerBinding::Wrap(aCx, this, aGivenProto); -} - -nsISupports* -FlyWebDiscoveryManager::GetParentObject() const -{ - return mParent; -} - -/* static */ already_AddRefed<FlyWebDiscoveryManager> -FlyWebDiscoveryManager::Constructor(const GlobalObject& aGlobal, ErrorResult& rv) -{ - RefPtr<FlyWebService> service = FlyWebService::GetOrCreate(); - if (!service) { - return nullptr; - } - - RefPtr<FlyWebDiscoveryManager> result = new FlyWebDiscoveryManager( - aGlobal.GetAsSupports(), service); - return result.forget(); -} - -void -FlyWebDiscoveryManager::ListServices(nsTArray<FlyWebDiscoveredService>& aServices) -{ - return mService->ListDiscoveredServices(aServices); -} - -uint32_t -FlyWebDiscoveryManager::StartDiscovery(FlyWebDiscoveryCallback& aCallback) -{ - uint32_t id = GenerateId(); - mCallbackMap.Put(id, &aCallback); - mService->RegisterDiscoveryManager(this); - return id; -} - -void -FlyWebDiscoveryManager::StopDiscovery(uint32_t aId) -{ - mCallbackMap.Remove(aId); - if (mCallbackMap.Count() == 0) { - mService->UnregisterDiscoveryManager(this); - } -} - -void -FlyWebDiscoveryManager::PairWithService(const nsAString& aServiceId, - FlyWebPairingCallback& aCallback) -{ - mService->PairWithService(aServiceId, aCallback); -} - -void -FlyWebDiscoveryManager::NotifyDiscoveredServicesChanged() -{ - nsTArray<FlyWebDiscoveredService> services; - ListServices(services); - Sequence<FlyWebDiscoveredService> servicesSeq; - servicesSeq.SwapElements(services); - for (auto iter = mCallbackMap.Iter(); !iter.Done(); iter.Next()) { - FlyWebDiscoveryCallback *callback = iter.UserData(); - ErrorResult err; - callback->OnDiscoveredServicesChanged(servicesSeq, err); - ENSURE_SUCCESS_VOID(err); - } -} - - -} // namespace dom -} // namespace mozilla diff --git a/dom/flyweb/FlyWebDiscoveryManager.h b/dom/flyweb/FlyWebDiscoveryManager.h deleted file mode 100644 index cb5f692f8..000000000 --- a/dom/flyweb/FlyWebDiscoveryManager.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- 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_FlyWebDiscoveryManager_h -#define mozilla_dom_FlyWebDiscoveryManager_h - -#include "nsISupportsImpl.h" -#include "mozilla/ErrorResult.h" -#include "nsRefPtrHashtable.h" -#include "nsWrapperCache.h" -#include "FlyWebDiscoveryManagerBinding.h" -#include "FlyWebService.h" - -namespace mozilla { -namespace dom { - -class FlyWebDiscoveryManager final : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FlyWebDiscoveryManager) - - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - nsISupports* GetParentObject() const; - - static already_AddRefed<FlyWebDiscoveryManager> Constructor(const GlobalObject& aGlobal, - ErrorResult& rv); - - void ListServices(nsTArray<FlyWebDiscoveredService>& aServices); - uint32_t StartDiscovery(FlyWebDiscoveryCallback& aCallback); - void StopDiscovery(uint32_t aId); - - void PairWithService(const nsAString& aServiceId, - FlyWebPairingCallback& callback); - - void NotifyDiscoveredServicesChanged(); - -private: - FlyWebDiscoveryManager(nsISupports* mParent, FlyWebService* aService); - ~FlyWebDiscoveryManager(); - - uint32_t GenerateId() { - return ++mNextId; - } - - nsCOMPtr<nsISupports> mParent; - RefPtr<FlyWebService> mService; - - uint32_t mNextId; - - nsRefPtrHashtable<nsUint32HashKey, FlyWebDiscoveryCallback> mCallbackMap; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FlyWebDiscoveryManager_h diff --git a/dom/flyweb/FlyWebPublishOptionsIPCSerializer.h b/dom/flyweb/FlyWebPublishOptionsIPCSerializer.h deleted file mode 100644 index fa1a44113..000000000 --- a/dom/flyweb/FlyWebPublishOptionsIPCSerializer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- 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_FlyWebPublishOptionsIPCSerialiser_h -#define mozilla_dom_FlyWebPublishOptionsIPCSerialiser_h - -#include "mozilla/dom/FlyWebPublishBinding.h" - -namespace IPC { - -template <> -struct ParamTraits<mozilla::dom::FlyWebPublishOptions> -{ - typedef mozilla::dom::FlyWebPublishOptions paramType; - - // Function to serialize a FlyWebPublishOptions - static void Write(Message *aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mUiUrl); - } - // Function to de-serialize a FlyWebPublishOptions - static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &(aResult->mUiUrl)); - } -}; - -} - -#endif // mozilla_dom_FlyWebPublishOptionsIPCSerialiser_h diff --git a/dom/flyweb/FlyWebPublishedServer.cpp b/dom/flyweb/FlyWebPublishedServer.cpp deleted file mode 100644 index 375df332f..000000000 --- a/dom/flyweb/FlyWebPublishedServer.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/* -*- 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 "mozilla/dom/FlyWebPublishedServerIPC.h" -#include "mozilla/dom/FlyWebPublishBinding.h" -#include "mozilla/dom/FlyWebService.h" -#include "mozilla/dom/Request.h" -#include "mozilla/dom/FlyWebServerEvents.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/InternalResponse.h" -#include "mozilla/ipc/IPCStreamUtils.h" -#include "mozilla/net/NeckoParent.h" -#include "mozilla/net/IPCTransportProvider.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/Preferences.h" -#include "mozilla/Unused.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsGlobalWindow.h" -#include "WebSocketChannel.h" - -namespace mozilla { -namespace dom { - -static LazyLogModule gFlyWebPublishedServerLog("FlyWebPublishedServer"); -#undef LOG_I -#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebPublishedServerLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#undef LOG_E -#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebPublishedServerLog, mozilla::LogLevel::Error, (__VA_ARGS__)) - -/******** FlyWebPublishedServer ********/ - -FlyWebPublishedServer::FlyWebPublishedServer(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions) - : mozilla::DOMEventTargetHelper(aOwner) - , mOwnerWindowID(aOwner ? aOwner->WindowID() : 0) - , mName(aName) - , mUiUrl(aOptions.mUiUrl) - , mIsRegistered(true) // Registered by the FlyWebService -{ -} - -void -FlyWebPublishedServer::LastRelease() -{ - // Make sure to unregister to avoid dangling pointers. Use the LastRelease - // hook rather than dtor since calling virtual functions during dtor - // wouldn't do what we want. Also, LastRelease is called earlier than dtor - // for CC objects. - Close(); -} - -JSObject* -FlyWebPublishedServer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return FlyWebPublishedServerBinding::Wrap(aCx, this, aGivenProto); -} - -void -FlyWebPublishedServer::Close() -{ - LOG_I("FlyWebPublishedServer::Close(%p)", this); - - // Unregister from server. - if (mIsRegistered) { - MOZ_ASSERT(FlyWebService::GetExisting()); - FlyWebService::GetExisting()->UnregisterServer(this); - mIsRegistered = false; - - DispatchTrustedEvent(NS_LITERAL_STRING("close")); - } -} - -void -FlyWebPublishedServer::FireFetchEvent(InternalRequest* aRequest) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - RefPtr<FlyWebFetchEvent> e = new FlyWebFetchEvent(this, - new Request(global, aRequest), - aRequest); - e->Init(this); - e->InitEvent(NS_LITERAL_STRING("fetch"), false, false); - - DispatchTrustedEvent(e); -} - -void -FlyWebPublishedServer::FireWebsocketEvent(InternalRequest* aConnectRequest) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - RefPtr<FlyWebFetchEvent> e = new FlyWebWebSocketEvent(this, - new Request(global, aConnectRequest), - aConnectRequest); - e->Init(this); - e->InitEvent(NS_LITERAL_STRING("websocket"), false, false); - - DispatchTrustedEvent(e); -} - -void -FlyWebPublishedServer::PublishedServerStarted(nsresult aStatus) -{ - LOG_I("FlyWebPublishedServer::PublishedServerStarted(%p)", this); - - RefPtr<FlyWebPublishPromise> promise = mPublishPromise.Ensure(__func__); - if (NS_SUCCEEDED(aStatus)) { - mPublishPromise.Resolve(this, __func__); - } else { - Close(); - mPublishPromise.Reject(aStatus, __func__); - } -} - -already_AddRefed<WebSocket> -FlyWebPublishedServer::OnWebSocketAccept(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - MOZ_ASSERT(aConnectRequest); - - LOG_I("FlyWebPublishedServer::OnWebSocketAccept(%p)", this); - - nsCOMPtr<nsITransportProvider> provider = - OnWebSocketAcceptInternal(aConnectRequest, - aProtocol, - aRv); - if (aRv.Failed()) { - return nullptr; - } - MOZ_ASSERT(provider); - - nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetOwner()); - AutoJSContext cx; - GlobalObject global(cx, nsGlobalWindow::Cast(window)->FastGetGlobalJSObject()); - - nsAutoCString extensions, negotiatedExtensions; - aConnectRequest->Headers()-> - GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions, aRv); - mozilla::net::ProcessServerWebSocketExtensions(extensions, - negotiatedExtensions); - - nsCString url; - aConnectRequest->GetURL(url); - Sequence<nsString> protocols; - if (aProtocol.WasPassed() && - !protocols.AppendElement(aProtocol.Value(), fallible)) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } - - return WebSocket::ConstructorCommon(global, - NS_ConvertUTF8toUTF16(url), - protocols, - provider, - negotiatedExtensions, - aRv); -} - -/******** FlyWebPublishedServerImpl ********/ - -NS_IMPL_ISUPPORTS_INHERITED0(FlyWebPublishedServerImpl, mozilla::DOMEventTargetHelper) - -FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions) - : FlyWebPublishedServer(aOwner, aName, aOptions) - , mHttpServer(new HttpServer()) -{ - LOG_I("FlyWebPublishedServerImpl::FlyWebPublishedServerImpl(%p)", this); -} - -void -FlyWebPublishedServerImpl::PermissionGranted(bool aGranted) -{ - LOG_I("FlyWebPublishedServerImpl::PermissionGranted(%b)", aGranted); - if (!aGranted) { - PublishedServerStarted(NS_ERROR_FAILURE); - return; - } - - mHttpServer->Init(-1, Preferences::GetBool("flyweb.use-tls", false), this); -} - -void -FlyWebPublishedServerImpl::Close() -{ - FlyWebPublishedServer::Close(); - - if (mMDNSCancelRegister) { - mMDNSCancelRegister->Cancel(NS_BINDING_ABORTED); - mMDNSCancelRegister = nullptr; - } - - if (mHttpServer) { - RefPtr<HttpServer> server = mHttpServer.forget(); - server->Close(); - } -} - -void -FlyWebPublishedServerImpl::OnServerStarted(nsresult aStatus) -{ - if (NS_SUCCEEDED(aStatus)) { - FlyWebService::GetOrCreate()->StartDiscoveryOf(this); - } else { - PublishedServerStarted(aStatus); - } -} - -void -FlyWebPublishedServerImpl::OnFetchResponse(InternalRequest* aRequest, - InternalResponse* aResponse) -{ - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResponse); - - LOG_I("FlyWebPublishedServerImpl::OnFetchResponse(%p)", this); - - if (mHttpServer) { - mHttpServer->SendResponse(aRequest, aResponse); - } -} - -void -FlyWebPublishedServerImpl::OnWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse) -{ - MOZ_ASSERT(aConnectRequest); - MOZ_ASSERT(aResponse); - - LOG_I("FlyWebPublishedMDNSServer::OnWebSocketResponse(%p)", this); - - if (mHttpServer) { - mHttpServer->SendWebSocketResponse(aConnectRequest, aResponse); - } -} - -already_AddRefed<nsITransportProvider> -FlyWebPublishedServerImpl::OnWebSocketAcceptInternal(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - LOG_I("FlyWebPublishedServerImpl::OnWebSocketAcceptInternal(%p)", this); - - if (!mHttpServer) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - return mHttpServer->AcceptWebSocket(aConnectRequest, - aProtocol, - aRv); -} - -/******** FlyWebPublishedServerChild ********/ - -FlyWebPublishedServerChild::FlyWebPublishedServerChild(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions) - : FlyWebPublishedServer(aOwner, aName, aOptions) - , mActorExists(false) -{ - LOG_I("FlyWebPublishedServerChild::FlyWebPublishedServerChild(%p)", this); - - // The matching release happens when the actor is destroyed, in - // ContentChild::DeallocPFlyWebPublishedServerChild - NS_ADDREF_THIS(); -} - -void -FlyWebPublishedServerChild::PermissionGranted(bool aGranted) -{ - if (!aGranted) { - PublishedServerStarted(NS_ERROR_FAILURE); - return; - } - - mActorExists = true; - FlyWebPublishOptions options; - options.mUiUrl = mUiUrl; - - // Proceed with initialization. - ContentChild::GetSingleton()-> - SendPFlyWebPublishedServerConstructor(this, mName, options); -} - -bool -FlyWebPublishedServerChild::RecvServerReady(const nsresult& aStatus) -{ - LOG_I("FlyWebPublishedServerChild::RecvServerReady(%p)", this); - MOZ_ASSERT(mActorExists); - - PublishedServerStarted(aStatus); - return true; -} - -bool -FlyWebPublishedServerChild::RecvServerClose() -{ - LOG_I("FlyWebPublishedServerChild::RecvServerClose(%p)", this); - MOZ_ASSERT(mActorExists); - - Close(); - - return true; -} - -bool -FlyWebPublishedServerChild::RecvFetchRequest(const IPCInternalRequest& aRequest, - const uint64_t& aRequestId) -{ - LOG_I("FlyWebPublishedServerChild::RecvFetchRequest(%p)", this); - MOZ_ASSERT(mActorExists); - - RefPtr<InternalRequest> request = new InternalRequest(aRequest); - mPendingRequests.Put(request, aRequestId); - FireFetchEvent(request); - - return true; -} - -bool -FlyWebPublishedServerChild::RecvWebSocketRequest(const IPCInternalRequest& aRequest, - const uint64_t& aRequestId, - PTransportProviderChild* aProvider) -{ - LOG_I("FlyWebPublishedServerChild::RecvWebSocketRequest(%p)", this); - MOZ_ASSERT(mActorExists); - - RefPtr<InternalRequest> request = new InternalRequest(aRequest); - mPendingRequests.Put(request, aRequestId); - - // Not addreffing here. The addref was already done when the - // PTransportProvider child constructor original ran. - mPendingTransportProviders.Put(aRequestId, - dont_AddRef(static_cast<TransportProviderChild*>(aProvider))); - - FireWebsocketEvent(request); - - return true; -} - -void -FlyWebPublishedServerChild::ActorDestroy(ActorDestroyReason aWhy) -{ - LOG_I("FlyWebPublishedServerChild::ActorDestroy(%p)", this); - - mActorExists = false; -} - -void -FlyWebPublishedServerChild::OnFetchResponse(InternalRequest* aRequest, - InternalResponse* aResponse) -{ - LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p)", this); - - if (!mActorExists) { - LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p) - No actor!", this); - return; - } - - uint64_t id = mPendingRequests.Get(aRequest); - MOZ_ASSERT(id); - mPendingRequests.Remove(aRequest); - - IPCInternalResponse ipcResp; - UniquePtr<mozilla::ipc::AutoIPCStream> autoStream; - nsIContentChild* cc = static_cast<ContentChild*>(Manager()); - aResponse->ToIPC(&ipcResp, cc, autoStream); - Unused << SendFetchResponse(ipcResp, id); - if (autoStream) { - autoStream->TakeOptionalValue(); - } -} - -already_AddRefed<nsITransportProvider> -FlyWebPublishedServerChild::OnWebSocketAcceptInternal(InternalRequest* aRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p)", this); - - if (!mActorExists) { - LOG_I("FlyWebPublishedServerChild::OnWebSocketAcceptInternal(%p) - No actor!", this); - return nullptr; - } - - uint64_t id = mPendingRequests.Get(aRequest); - MOZ_ASSERT(id); - mPendingRequests.Remove(aRequest); - - RefPtr<TransportProviderChild> provider; - mPendingTransportProviders.Remove(id, getter_AddRefs(provider)); - - nsString protocol; - if (aProtocol.WasPassed()) { - protocol = aProtocol.Value(); - - nsAutoCString reqProtocols; - aRequest->Headers()-> - GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv); - if (!ContainsToken(reqProtocols, NS_ConvertUTF16toUTF8(protocol))) { - // Should throw a better error here - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - } else { - protocol.SetIsVoid(true); - } - - Unused << SendWebSocketAccept(protocol, id); - - return provider.forget(); -} - -void -FlyWebPublishedServerChild::OnWebSocketResponse(InternalRequest* aRequest, - InternalResponse* aResponse) -{ - LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p)", this); - - if (!mActorExists) { - LOG_I("FlyWebPublishedServerChild::OnFetchResponse(%p) - No actor!", this); - return; - } - - uint64_t id = mPendingRequests.Get(aRequest); - MOZ_ASSERT(id); - mPendingRequests.Remove(aRequest); - - mPendingTransportProviders.Remove(id); - - IPCInternalResponse ipcResp; - UniquePtr<mozilla::ipc::AutoIPCStream> autoStream; - nsIContentChild* cc = static_cast<ContentChild*>(Manager()); - aResponse->ToIPC(&ipcResp, cc, autoStream); - - Unused << SendWebSocketResponse(ipcResp, id); - if (autoStream) { - autoStream->TakeOptionalValue(); - } -} - -void -FlyWebPublishedServerChild::Close() -{ - LOG_I("FlyWebPublishedServerChild::Close(%p)", this); - - FlyWebPublishedServer::Close(); - - if (mActorExists) { - LOG_I("FlyWebPublishedServerChild::Close - sending __delete__ (%p)", this); - - Send__delete__(this); - } -} - -/******** FlyWebPublishedServerParent ********/ - -NS_IMPL_ISUPPORTS(FlyWebPublishedServerParent, nsIDOMEventListener) - -FlyWebPublishedServerParent::FlyWebPublishedServerParent(const nsAString& aName, - const FlyWebPublishOptions& aOptions) - : mActorDestroyed(false) - , mNextRequestId(1) -{ - LOG_I("FlyWebPublishedServerParent::FlyWebPublishedServerParent(%p)", this); - - RefPtr<FlyWebService> service = FlyWebService::GetOrCreate(); - if (!service) { - Unused << SendServerReady(NS_ERROR_FAILURE); - return; - } - - RefPtr<FlyWebPublishPromise> mozPromise = - service->PublishServer(aName, aOptions, nullptr); - if (!mozPromise) { - Unused << SendServerReady(NS_ERROR_FAILURE); - return; - } - - RefPtr<FlyWebPublishedServerParent> self = this; - - mozPromise->Then( - AbstractThread::MainThread(), - __func__, - [this, self] (FlyWebPublishedServer* aServer) { - mPublishedServer = static_cast<FlyWebPublishedServerImpl*>(aServer); - if (mActorDestroyed) { - mPublishedServer->Close(); - return; - } - - mPublishedServer->AddEventListener(NS_LITERAL_STRING("fetch"), - this, false, false, 2); - mPublishedServer->AddEventListener(NS_LITERAL_STRING("websocket"), - this, false, false, 2); - mPublishedServer->AddEventListener(NS_LITERAL_STRING("close"), - this, false, false, 2); - Unused << SendServerReady(NS_OK); - }, - [this, self] (nsresult aStatus) { - MOZ_ASSERT(NS_FAILED(aStatus)); - if (!mActorDestroyed) { - Unused << SendServerReady(aStatus); - } - }); -} - -NS_IMETHODIMP -FlyWebPublishedServerParent::HandleEvent(nsIDOMEvent* aEvent) -{ - if (mActorDestroyed) { - return NS_OK; - } - - nsAutoString type; - aEvent->GetType(type); - if (type.EqualsLiteral("close")) { - Unused << SendServerClose(); - return NS_OK; - } - - if (type.EqualsLiteral("fetch")) { - RefPtr<InternalRequest> request = - static_cast<FlyWebFetchEvent*>(aEvent)->Request()->GetInternalRequest(); - uint64_t id = mNextRequestId++; - mPendingRequests.Put(id, request); - - IPCInternalRequest ipcReq; - request->ToIPC(&ipcReq); - Unused << SendFetchRequest(ipcReq, id); - return NS_OK; - } - - if (type.EqualsLiteral("websocket")) { - RefPtr<InternalRequest> request = - static_cast<FlyWebWebSocketEvent*>(aEvent)->Request()->GetInternalRequest(); - uint64_t id = mNextRequestId++; - mPendingRequests.Put(id, request); - - nsTArray<PNeckoParent*> neckoParents; - Manager()->ManagedPNeckoParent(neckoParents); - if (neckoParents.Length() != 1) { - MOZ_CRASH("Expected exactly 1 PNeckoParent instance per PNeckoChild"); - } - - RefPtr<TransportProviderParent> provider = - static_cast<TransportProviderParent*>( - neckoParents[0]->SendPTransportProviderConstructor()); - - IPCInternalRequest ipcReq; - request->ToIPC(&ipcReq); - Unused << SendWebSocketRequest(ipcReq, id, provider); - - mPendingTransportProviders.Put(id, provider.forget()); - return NS_OK; - } - - MOZ_CRASH("Unknown event type"); - - return NS_OK; -} - -bool -FlyWebPublishedServerParent::RecvFetchResponse(const IPCInternalResponse& aResponse, - const uint64_t& aRequestId) -{ - MOZ_ASSERT(!mActorDestroyed); - - RefPtr<InternalRequest> request; - mPendingRequests.Remove(aRequestId, getter_AddRefs(request)); - if (!request) { - static_cast<ContentParent*>(Manager())->KillHard("unknown request id"); - return false; - } - - RefPtr<InternalResponse> response = InternalResponse::FromIPC(aResponse); - - mPublishedServer->OnFetchResponse(request, response); - - return true; -} - -bool -FlyWebPublishedServerParent::RecvWebSocketResponse(const IPCInternalResponse& aResponse, - const uint64_t& aRequestId) -{ - MOZ_ASSERT(!mActorDestroyed); - - mPendingTransportProviders.Remove(aRequestId); - - RefPtr<InternalRequest> request; - mPendingRequests.Remove(aRequestId, getter_AddRefs(request)); - if (!request) { - static_cast<ContentParent*>(Manager())->KillHard("unknown websocket request id"); - return false; - } - - RefPtr<InternalResponse> response = InternalResponse::FromIPC(aResponse); - - mPublishedServer->OnWebSocketResponse(request, response); - - return true; -} - -bool -FlyWebPublishedServerParent::RecvWebSocketAccept(const nsString& aProtocol, - const uint64_t& aRequestId) -{ - MOZ_ASSERT(!mActorDestroyed); - - RefPtr<TransportProviderParent> providerIPC; - mPendingTransportProviders.Remove(aRequestId, getter_AddRefs(providerIPC)); - - RefPtr<InternalRequest> request; - mPendingRequests.Remove(aRequestId, getter_AddRefs(request)); - - if (!request || !providerIPC) { - static_cast<ContentParent*>(Manager())->KillHard("unknown websocket request id"); - return false; - } - - Optional<nsAString> protocol; - if (!aProtocol.IsVoid()) { - protocol = &aProtocol; - } - - ErrorResult result; - nsCOMPtr<nsITransportProvider> providerServer = - mPublishedServer->OnWebSocketAcceptInternal(request, protocol, result); - if (result.Failed()) { - return false; - } - - providerServer->SetListener(providerIPC); - - return true; -} - -void -FlyWebPublishedServerParent::ActorDestroy(ActorDestroyReason aWhy) -{ - LOG_I("FlyWebPublishedServerParent::ActorDestroy(%p)", this); - - mActorDestroyed = true; -} - -bool -FlyWebPublishedServerParent::Recv__delete__() -{ - LOG_I("FlyWebPublishedServerParent::Recv__delete__(%p)", this); - MOZ_ASSERT(!mActorDestroyed); - - if (mPublishedServer) { - mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("fetch"), - this, false); - mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("websocket"), - this, false); - mPublishedServer->RemoveEventListener(NS_LITERAL_STRING("close"), - this, false); - mPublishedServer->Close(); - mPublishedServer = nullptr; - } - return true; -} - -} // namespace dom -} // namespace mozilla - - diff --git a/dom/flyweb/FlyWebPublishedServer.h b/dom/flyweb/FlyWebPublishedServer.h deleted file mode 100644 index ec3a685ec..000000000 --- a/dom/flyweb/FlyWebPublishedServer.h +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- 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_FlyWebPublishedServer_h -#define mozilla_dom_FlyWebPublishedServer_h - -#include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/MozPromise.h" - -class nsPIDOMWindowInner; -class nsITransportProvider; - -namespace mozilla { - -class ErrorResult; - -namespace dom { - -class InternalResponse; -class InternalRequest; -class WebSocket; -struct FlyWebPublishOptions; -class FlyWebPublishedServer; - -typedef MozPromise<RefPtr<FlyWebPublishedServer>, nsresult, false> - FlyWebPublishPromise; - -class FlyWebPublishedServer : public mozilla::DOMEventTargetHelper -{ -public: - FlyWebPublishedServer(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions); - - virtual void LastRelease() override; - - virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; - - uint64_t OwnerWindowID() const { - return mOwnerWindowID; - } - - void GetName(nsAString& aName) - { - aName = mName; - } - nsAString& Name() - { - return mName; - } - - void GetUiUrl(nsAString& aUiUrl) - { - aUiUrl = mUiUrl; - } - - virtual void PermissionGranted(bool aGranted) = 0; - - virtual void OnFetchResponse(InternalRequest* aRequest, - InternalResponse* aResponse) = 0; - already_AddRefed<WebSocket> - OnWebSocketAccept(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv); - virtual void OnWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse) = 0; - virtual already_AddRefed<nsITransportProvider> - OnWebSocketAcceptInternal(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) = 0; - - virtual void Close(); - - void FireFetchEvent(InternalRequest* aRequest); - void FireWebsocketEvent(InternalRequest* aConnectRequest); - void PublishedServerStarted(nsresult aStatus); - - IMPL_EVENT_HANDLER(fetch) - IMPL_EVENT_HANDLER(websocket) - IMPL_EVENT_HANDLER(close) - - already_AddRefed<FlyWebPublishPromise> - GetPublishPromise() - { - return mPublishPromise.Ensure(__func__); - } - -protected: - virtual ~FlyWebPublishedServer() - { - MOZ_ASSERT(!mIsRegistered, "Subclass dtor forgot to call Close()"); - } - - uint64_t mOwnerWindowID; - MozPromiseHolder<FlyWebPublishPromise> mPublishPromise; - - nsString mName; - nsString mUiUrl; - - bool mIsRegistered; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FlyWebPublishedServer_h diff --git a/dom/flyweb/FlyWebPublishedServerIPC.h b/dom/flyweb/FlyWebPublishedServerIPC.h deleted file mode 100644 index 942c7847e..000000000 --- a/dom/flyweb/FlyWebPublishedServerIPC.h +++ /dev/null @@ -1,172 +0,0 @@ -/* -*- 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_FlyWebPublishedServerIPC_h -#define mozilla_dom_FlyWebPublishedServerIPC_h - -#include "HttpServer.h" -#include "mozilla/dom/FlyWebPublishedServer.h" -#include "mozilla/dom/PFlyWebPublishedServerParent.h" -#include "mozilla/dom/PFlyWebPublishedServerChild.h" -#include "mozilla/MozPromise.h" -#include "nsICancelable.h" -#include "nsIDOMEventListener.h" -#include "nsISupportsImpl.h" - -class nsPIDOMWindowInner; - -namespace mozilla { -namespace net { -class TransportProviderParent; -class TransportProviderChild; -} - -namespace dom { - -class FlyWebPublishedServerParent; - -class FlyWebPublishedServerImpl final : public FlyWebPublishedServer - , public HttpServerListener -{ -public: - FlyWebPublishedServerImpl(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions); - - NS_DECL_ISUPPORTS_INHERITED - - int32_t Port() - { - return mHttpServer ? mHttpServer->GetPort() : 0; - } - void GetCertKey(nsACString& aKey) { - if (mHttpServer) { - mHttpServer->GetCertKey(aKey); - } else { - aKey.Truncate(); - } - } - - virtual void PermissionGranted(bool aGranted) override; - virtual void OnFetchResponse(InternalRequest* aRequest, - InternalResponse* aResponse) override; - virtual void OnWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse) override; - virtual already_AddRefed<nsITransportProvider> - OnWebSocketAcceptInternal(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) override; - - void SetCancelRegister(nsICancelable* aCancelRegister) - { - mMDNSCancelRegister = aCancelRegister; - } - - virtual void Close() override; - - // HttpServerListener - virtual void OnServerStarted(nsresult aStatus) override; - virtual void OnRequest(InternalRequest* aRequest) override - { - FireFetchEvent(aRequest); - } - virtual void OnWebSocket(InternalRequest* aConnectRequest) override - { - FireWebsocketEvent(aConnectRequest); - } - virtual void OnServerClose() override - { - mHttpServer = nullptr; - Close(); - } - -private: - ~FlyWebPublishedServerImpl() {} - - RefPtr<HttpServer> mHttpServer; - nsCOMPtr<nsICancelable> mMDNSCancelRegister; - RefPtr<FlyWebPublishedServerParent> mServerParent; -}; - -class FlyWebPublishedServerChild final : public FlyWebPublishedServer - , public PFlyWebPublishedServerChild -{ -public: - FlyWebPublishedServerChild(nsPIDOMWindowInner* aOwner, - const nsAString& aName, - const FlyWebPublishOptions& aOptions); - - virtual void PermissionGranted(bool aGranted) override; - virtual bool RecvServerReady(const nsresult& aStatus) override; - virtual bool RecvServerClose() override; - virtual bool RecvFetchRequest(const IPCInternalRequest& aRequest, - const uint64_t& aRequestId) override; - virtual bool RecvWebSocketRequest(const IPCInternalRequest& aRequest, - const uint64_t& aRequestId, - PTransportProviderChild* aProvider) override; - - virtual void OnFetchResponse(InternalRequest* aRequest, - InternalResponse* aResponse) override; - virtual void OnWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse) override; - virtual already_AddRefed<nsITransportProvider> - OnWebSocketAcceptInternal(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) override; - - virtual void Close() override; - - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - -private: - ~FlyWebPublishedServerChild() {} - - nsDataHashtable<nsRefPtrHashKey<InternalRequest>, uint64_t> mPendingRequests; - nsRefPtrHashtable<nsUint64HashKey, TransportProviderChild> - mPendingTransportProviders; - bool mActorExists; -}; - -class FlyWebPublishedServerParent final : public PFlyWebPublishedServerParent - , public nsIDOMEventListener -{ -public: - FlyWebPublishedServerParent(const nsAString& aName, - const FlyWebPublishOptions& aOptions); - - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMEVENTLISTENER - -private: - virtual void - ActorDestroy(ActorDestroyReason aWhy) override; - - virtual bool - Recv__delete__() override; - virtual bool - RecvFetchResponse(const IPCInternalResponse& aResponse, - const uint64_t& aRequestId) override; - virtual bool - RecvWebSocketResponse(const IPCInternalResponse& aResponse, - const uint64_t& aRequestId) override; - virtual bool - RecvWebSocketAccept(const nsString& aProtocol, - const uint64_t& aRequestId) override; - - ~FlyWebPublishedServerParent() {} - - bool mActorDestroyed; - uint64_t mNextRequestId; - nsRefPtrHashtable<nsUint64HashKey, InternalRequest> mPendingRequests; - nsRefPtrHashtable<nsUint64HashKey, TransportProviderParent> - mPendingTransportProviders; - RefPtr<FlyWebPublishedServerImpl> mPublishedServer; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FlyWebPublishedServerIPC_h diff --git a/dom/flyweb/FlyWebServerEvents.cpp b/dom/flyweb/FlyWebServerEvents.cpp deleted file mode 100644 index fe774ffb0..000000000 --- a/dom/flyweb/FlyWebServerEvents.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "mozilla/dom/EventBinding.h" -#include "mozilla/dom/FlyWebFetchEventBinding.h" -#include "mozilla/dom/FlyWebPublishedServer.h" -#include "mozilla/dom/FlyWebServerEvents.h" -#include "mozilla/dom/FlyWebWebSocketEventBinding.h" -#include "mozilla/dom/Nullable.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/Response.h" - -#include "js/GCAPI.h" - -namespace mozilla { -namespace dom { - - -NS_IMPL_CYCLE_COLLECTION_CLASS(FlyWebFetchEvent) - -NS_IMPL_ADDREF_INHERITED(FlyWebFetchEvent, Event) -NS_IMPL_RELEASE_INHERITED(FlyWebFetchEvent, Event) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FlyWebFetchEvent, Event) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FlyWebFetchEvent, Event) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FlyWebFetchEvent, Event) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FlyWebFetchEvent) -NS_INTERFACE_MAP_END_INHERITING(Event) - -FlyWebFetchEvent::FlyWebFetchEvent(FlyWebPublishedServer* aServer, - class Request* aRequest, - InternalRequest* aInternalRequest) - : Event(aServer, nullptr, nullptr) - , mRequest(aRequest) - , mInternalRequest(aInternalRequest) - , mServer(aServer) - , mResponded(false) -{ - MOZ_ASSERT(aServer); - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aInternalRequest); -} - -FlyWebFetchEvent::~FlyWebFetchEvent() -{ -} - -JSObject* -FlyWebFetchEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return FlyWebFetchEventBinding::Wrap(aCx, this, aGivenProto); -} - -void -FlyWebFetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv) -{ - if (mResponded) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; - } - - mResponded = true; - - aArg.AppendNativeHandler(this); -} - -void -FlyWebFetchEvent::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) -{ - RefPtr<Response> response; - if (aValue.isObject()) { - UNWRAP_OBJECT(Response, &aValue.toObject(), response); - } - - RefPtr<InternalResponse> intResponse; - if (response && response->Type() != ResponseType::Opaque) { - intResponse = response->GetInternalResponse(); - } - - if (!intResponse) { - intResponse = InternalResponse::NetworkError(); - } - - NotifyServer(intResponse); -} - -void -FlyWebFetchEvent::NotifyServer(InternalResponse* aResponse) -{ - mServer->OnFetchResponse(mInternalRequest, aResponse); -} - -void -FlyWebFetchEvent::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) -{ - RefPtr<InternalResponse> err = InternalResponse::NetworkError(); - - NotifyServer(err); -} - -JSObject* -FlyWebWebSocketEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) -{ - return FlyWebWebSocketEventBinding::Wrap(aCx, this, aGivenProto); -} - -already_AddRefed<WebSocket> -FlyWebWebSocketEvent::Accept(const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - if (mResponded) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - - mResponded = true; - - return mServer->OnWebSocketAccept(mInternalRequest, aProtocol, aRv); -} - - -void -FlyWebWebSocketEvent::NotifyServer(InternalResponse* aResponse) -{ - mServer->OnWebSocketResponse(mInternalRequest, aResponse); -} - - -} // namespace dom -} // namespace mozilla diff --git a/dom/flyweb/FlyWebServerEvents.h b/dom/flyweb/FlyWebServerEvents.h deleted file mode 100644 index f00e86018..000000000 --- a/dom/flyweb/FlyWebServerEvents.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_FlyWebFetchEvent_h -#define mozilla_dom_FlyWebFetchEvent_h - -#include "mozilla/Attributes.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/Event.h" -#include "mozilla/dom/FlyWebFetchEventBinding.h" -#include "mozilla/dom/PromiseNativeHandler.h" -#include "mozilla/dom/WebSocket.h" - -struct JSContext; -namespace mozilla { -namespace dom { - -class Request; -class Response; -class FlyWebPublishedServer; -class InternalRequest; -class InternalResponse; - -class FlyWebFetchEvent : public Event - , public PromiseNativeHandler -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FlyWebFetchEvent, Event) - - virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - - virtual void - ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; - virtual void - RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; - - class Request* Request() const - { - return mRequest; - } - - void RespondWith(Promise& aArg, ErrorResult& aRv); - - FlyWebFetchEvent(FlyWebPublishedServer* aServer, - class Request* aRequest, - InternalRequest* aInternalRequest); - -protected: - virtual ~FlyWebFetchEvent(); - - virtual void NotifyServer(InternalResponse* aResponse); - - RefPtr<class Request> mRequest; - RefPtr<InternalRequest> mInternalRequest; - RefPtr<FlyWebPublishedServer> mServer; - - bool mResponded; -}; - -class FlyWebWebSocketEvent final : public FlyWebFetchEvent -{ -public: - FlyWebWebSocketEvent(FlyWebPublishedServer* aServer, - class Request* aRequest, - InternalRequest* aInternalRequest) - : FlyWebFetchEvent(aServer, aRequest, aInternalRequest) - {} - - virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; - - already_AddRefed<WebSocket> Accept(const Optional<nsAString>& aProtocol, - ErrorResult& aRv); - -private: - ~FlyWebWebSocketEvent() {}; - - virtual void NotifyServer(InternalResponse* aResponse) override; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FlyWebFetchEvent_h diff --git a/dom/flyweb/FlyWebService.cpp b/dom/flyweb/FlyWebService.cpp deleted file mode 100644 index 5f3b0d66f..000000000 --- a/dom/flyweb/FlyWebService.cpp +++ /dev/null @@ -1,1310 +0,0 @@ -/* -*- 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 "mozilla/dom/FlyWebService.h" -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/ScopeExit.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/FlyWebPublishedServerIPC.h" -#include "mozilla/AddonPathService.h" -#include "nsISocketTransportService.h" -#include "mdns/libmdns/nsDNSServiceInfo.h" -#include "nsIUUIDGenerator.h" -#include "nsStandardURL.h" -#include "mozilla/Services.h" -#include "nsISupportsPrimitives.h" -#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h" -#include "prnetdb.h" -#include "DNS.h" -#include "nsContentPermissionHelper.h" -#include "nsSocketTransportService2.h" -#include "nsSocketTransport2.h" -#include "nsHashPropertyBag.h" -#include "nsNetUtil.h" -#include "nsISimpleEnumerator.h" -#include "nsIProperty.h" -#include "nsICertOverrideService.h" - -namespace mozilla { -namespace dom { - -struct FlyWebPublishOptions; - -static LazyLogModule gFlyWebServiceLog("FlyWebService"); -#undef LOG_I -#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -#undef LOG_E -#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Error, (__VA_ARGS__)) - -#undef LOG_TEST_I -#define LOG_TEST_I(...) MOZ_LOG_TEST(mozilla::dom::gFlyWebServiceLog, mozilla::LogLevel::Debug) - -class FlyWebPublishServerPermissionCheck final - : public nsIContentPermissionRequest - , public nsIRunnable -{ -public: - NS_DECL_ISUPPORTS - - FlyWebPublishServerPermissionCheck(const nsCString& aServiceName, uint64_t aWindowID, - FlyWebPublishedServer* aServer) - : mServiceName(aServiceName) - , mWindowID(aWindowID) - , mServer(aServer) - {} - - uint64_t WindowID() const - { - return mWindowID; - } - - NS_IMETHOD Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - - nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID); - if (!globalWindow) { - return Cancel(); - } - mWindow = globalWindow->AsInner(); - if (NS_WARN_IF(!mWindow)) { - return Cancel(); - } - - nsCOMPtr<nsIDocument> doc = mWindow->GetDoc(); - if (NS_WARN_IF(!doc)) { - return Cancel(); - } - - mPrincipal = doc->NodePrincipal(); - MOZ_ASSERT(mPrincipal); - - mRequester = new nsContentPermissionRequester(mWindow); - return nsContentPermissionUtils::AskPermission(this, mWindow); - } - - NS_IMETHOD Cancel() override - { - Resolve(false); - return NS_OK; - } - - NS_IMETHOD Allow(JS::HandleValue aChoices) override - { - MOZ_ASSERT(aChoices.isUndefined()); - Resolve(true); - return NS_OK; - } - - NS_IMETHOD GetTypes(nsIArray** aTypes) override - { - nsTArray<nsString> emptyOptions; - return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("flyweb-publish-server"), - NS_LITERAL_CSTRING("unused"), emptyOptions, aTypes); - } - - NS_IMETHOD GetRequester(nsIContentPermissionRequester** aRequester) override - { - NS_ENSURE_ARG_POINTER(aRequester); - nsCOMPtr<nsIContentPermissionRequester> requester = mRequester; - requester.forget(aRequester); - return NS_OK; - } - - NS_IMETHOD GetPrincipal(nsIPrincipal** aRequestingPrincipal) override - { - NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); - return NS_OK; - } - - NS_IMETHOD GetWindow(mozIDOMWindow** aRequestingWindow) override - { - NS_IF_ADDREF(*aRequestingWindow = mWindow); - return NS_OK; - } - - NS_IMETHOD GetElement(nsIDOMElement** aRequestingElement) override - { - *aRequestingElement = nullptr; - return NS_OK; - } - -private: - void Resolve(bool aResolve) - { - mServer->PermissionGranted(aResolve); - } - - virtual ~FlyWebPublishServerPermissionCheck() = default; - - nsCString mServiceName; - uint64_t mWindowID; - RefPtr<FlyWebPublishedServer> mServer; - nsCOMPtr<nsPIDOMWindowInner> mWindow; - nsCOMPtr<nsIPrincipal> mPrincipal; - nsCOMPtr<nsIContentPermissionRequester> mRequester; -}; - -NS_IMPL_ISUPPORTS(FlyWebPublishServerPermissionCheck, - nsIContentPermissionRequest, - nsIRunnable) - -class FlyWebMDNSService final - : public nsIDNSServiceDiscoveryListener - , public nsIDNSServiceResolveListener - , public nsIDNSRegistrationListener - , public nsITimerCallback -{ - friend class FlyWebService; - -private: - enum DiscoveryState { - DISCOVERY_IDLE, - DISCOVERY_STARTING, - DISCOVERY_RUNNING, - DISCOVERY_STOPPING - }; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER - NS_DECL_NSIDNSSERVICERESOLVELISTENER - NS_DECL_NSIDNSREGISTRATIONLISTENER - NS_DECL_NSITIMERCALLBACK - - explicit FlyWebMDNSService(FlyWebService* aService, - const nsACString& aServiceType); - -private: - virtual ~FlyWebMDNSService() = default; - - nsresult Init(); - nsresult StartDiscovery(); - nsresult StopDiscovery(); - - void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices); - bool HasService(const nsAString& aServiceId); - nsresult PairWithService(const nsAString& aServiceId, - UniquePtr<FlyWebService::PairedInfo>& aInfo); - - nsresult StartDiscoveryOf(FlyWebPublishedServerImpl* aServer); - - void EnsureDiscoveryStarted(); - void EnsureDiscoveryStopped(); - - // Cycle-breaking link to manager. - FlyWebService* mService; - nsCString mServiceType; - - // Indicates the desired state of the system. If mDiscoveryActive is true, - // it indicates that backend discovery "should be happening", and discovery - // events should be forwarded to listeners. - // If false, the backend discovery "should be idle", and any discovery events - // that show up should not be forwarded to listeners. - bool mDiscoveryActive; - - uint32_t mNumConsecutiveStartDiscoveryFailures; - - // Represents the internal discovery state as it relates to nsDNSServiceDiscovery. - // When mDiscoveryActive is true, this state will periodically loop from - // (IDLE => STARTING => RUNNING => STOPPING => IDLE). - DiscoveryState mDiscoveryState; - - nsCOMPtr<nsITimer> mDiscoveryStartTimer; - nsCOMPtr<nsITimer> mDiscoveryStopTimer; - nsCOMPtr<nsIDNSServiceDiscovery> mDNSServiceDiscovery; - nsCOMPtr<nsICancelable> mCancelDiscovery; - nsTHashtable<nsStringHashKey> mNewServiceSet; - - struct DiscoveredInfo - { - explicit DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo); - FlyWebDiscoveredService mService; - nsCOMPtr<nsIDNSServiceInfo> mDNSServiceInfo; - }; - nsClassHashtable<nsStringHashKey, DiscoveredInfo> mServiceMap; -}; - -void -LogDNSInfo(nsIDNSServiceInfo* aServiceInfo, const char* aFunc) -{ - if (!LOG_TEST_I()) { - return; - } - - nsCString tmp; - aServiceInfo->GetServiceName(tmp); - LOG_I("%s: serviceName=%s", aFunc, tmp.get()); - - aServiceInfo->GetHost(tmp); - LOG_I("%s: host=%s", aFunc, tmp.get()); - - aServiceInfo->GetAddress(tmp); - LOG_I("%s: address=%s", aFunc, tmp.get()); - - uint16_t port = -2; - aServiceInfo->GetPort(&port); - LOG_I("%s: port=%d", aFunc, (int)port); - - nsCOMPtr<nsIPropertyBag2> attributes; - aServiceInfo->GetAttributes(getter_AddRefs(attributes)); - if (!attributes) { - LOG_I("%s: no attributes", aFunc); - } else { - nsCOMPtr<nsISimpleEnumerator> enumerator; - attributes->GetEnumerator(getter_AddRefs(enumerator)); - MOZ_ASSERT(enumerator); - - LOG_I("%s: attributes start", aFunc); - - bool hasMoreElements; - while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && - hasMoreElements) { - nsCOMPtr<nsISupports> element; - MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element))); - nsCOMPtr<nsIProperty> property = do_QueryInterface(element); - MOZ_ASSERT(property); - - nsAutoString name; - nsCOMPtr<nsIVariant> value; - MOZ_ALWAYS_SUCCEEDS(property->GetName(name)); - MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value))); - - nsAutoCString str; - nsresult rv = value->GetAsACString(str); - if (NS_SUCCEEDED(rv)) { - LOG_I("%s: attribute name=%s value=%s", aFunc, - NS_ConvertUTF16toUTF8(name).get(), str.get()); - } else { - uint16_t type; - MOZ_ALWAYS_SUCCEEDS(value->GetDataType(&type)); - LOG_I("%s: attribute *unstringifiable* name=%s type=%d", aFunc, - NS_ConvertUTF16toUTF8(name).get(), (int)type); - } - } - - LOG_I("%s: attributes end", aFunc); - } -} - -NS_IMPL_ISUPPORTS(FlyWebMDNSService, - nsIDNSServiceDiscoveryListener, - nsIDNSServiceResolveListener, - nsIDNSRegistrationListener, - nsITimerCallback) - -FlyWebMDNSService::FlyWebMDNSService( - FlyWebService* aService, - const nsACString& aServiceType) - : mService(aService) - , mServiceType(aServiceType) - , mDiscoveryActive(false) - , mNumConsecutiveStartDiscoveryFailures(0) - , mDiscoveryState(DISCOVERY_IDLE) -{} - -nsresult -FlyWebMDNSService::OnDiscoveryStarted(const nsACString& aServiceType) -{ - MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING); - mDiscoveryState = DISCOVERY_RUNNING; - // Reset consecutive start discovery failures. - mNumConsecutiveStartDiscoveryFailures = 0; - LOG_I("==========================================="); - LOG_I("MDNSService::OnDiscoveryStarted(%s)", PromiseFlatCString(aServiceType).get()); - LOG_I("==========================================="); - - // Clear the new service array. - mNewServiceSet.Clear(); - - // If service discovery is inactive, then stop network discovery immediately. - if (!mDiscoveryActive) { - // Set the stop timer to fire immediately. - Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT))); - return NS_OK; - } - - // Otherwise, set the stop timer to fire in 5 seconds. - Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStopTimer->InitWithCallback(this, 5 * 1000, nsITimer::TYPE_ONE_SHOT))); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnDiscoveryStopped(const nsACString& aServiceType) -{ - LOG_I("///////////////////////////////////////////"); - LOG_I("MDNSService::OnDiscoveryStopped(%s)", PromiseFlatCString(aServiceType).get()); - LOG_I("///////////////////////////////////////////"); - MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING); - mDiscoveryState = DISCOVERY_IDLE; - - // If service discovery is inactive, then discard all results and do not proceed. - if (!mDiscoveryActive) { - mServiceMap.Clear(); - mNewServiceSet.Clear(); - return NS_OK; - } - - // Process the service map, add to the pair map. - for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) { - DiscoveredInfo* service = iter.UserData(); - - if (!mNewServiceSet.Contains(service->mService.mServiceId)) { - iter.Remove(); - } - } - - // Notify FlyWebService of changed service list. - mService->NotifyDiscoveredServicesChanged(); - - // Start discovery again immediately. - Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT))); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnServiceFound(nsIDNSServiceInfo* aServiceInfo) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceFound"); - - // If discovery is not active, don't do anything with the result. - // If there is no discovery underway, ignore this. - if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) { - return NS_OK; - } - - // Discovery is underway - resolve the service. - nsresult rv = mDNSServiceDiscovery->ResolveService(aServiceInfo, this); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnServiceLost(nsIDNSServiceInfo* aServiceInfo) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceLost"); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnStartDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode) -{ - LOG_E("MDNSService::OnStartDiscoveryFailed(%s): %d", PromiseFlatCString(aServiceType).get(), (int) aErrorCode); - - MOZ_ASSERT(mDiscoveryState == DISCOVERY_STARTING); - mDiscoveryState = DISCOVERY_IDLE; - mNumConsecutiveStartDiscoveryFailures++; - - // If discovery is active, and the number of consecutive failures is < 3, try starting again. - if (mDiscoveryActive && mNumConsecutiveStartDiscoveryFailures < 3) { - Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT))); - } - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnStopDiscoveryFailed(const nsACString& aServiceType, int32_t aErrorCode) -{ - LOG_E("MDNSService::OnStopDiscoveryFailed(%s)", PromiseFlatCString(aServiceType).get()); - MOZ_ASSERT(mDiscoveryState == DISCOVERY_STOPPING); - mDiscoveryState = DISCOVERY_IDLE; - - // If discovery is active, start discovery again immediately. - if (mDiscoveryActive) { - Unused << NS_WARN_IF(NS_FAILED(mDiscoveryStartTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT))); - } - - return NS_OK; -} - -static bool -IsAcceptableServiceAddress(const nsCString& addr) -{ - PRNetAddr prNetAddr; - PRStatus status = PR_StringToNetAddr(addr.get(), &prNetAddr); - if (status == PR_FAILURE) { - return false; - } - // Only allow ipv4 addreses for now. - return prNetAddr.raw.family == PR_AF_INET; -} - -nsresult -FlyWebMDNSService::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceResolved"); - - // If discovery is not active, don't do anything with the result. - // If there is no discovery underway, ignore this resolve. - if (!mDiscoveryActive || mDiscoveryState != DISCOVERY_RUNNING) { - return NS_OK; - } - - nsresult rv; - - nsCString address; - rv = aServiceInfo->GetAddress(address); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!IsAcceptableServiceAddress(address)) { - return NS_OK; - } - - // Create a new serviceInfo and stuff it in the new service array. - UniquePtr<DiscoveredInfo> svc(new DiscoveredInfo(aServiceInfo)); - mNewServiceSet.PutEntry(svc->mService.mServiceId); - - DiscoveredInfo* existingSvc = - mServiceMap.Get(svc->mService.mServiceId); - if (existingSvc) { - // Update the underlying DNS service info, but leave the old object in place. - existingSvc->mDNSServiceInfo = aServiceInfo; - } else { - DiscoveredInfo* info = svc.release(); - mServiceMap.Put(info->mService.mServiceId, info); - } - - // Notify FlyWebService of changed service list. - mService->NotifyDiscoveredServicesChanged(); - - return NS_OK; -} - -FlyWebMDNSService::DiscoveredInfo::DiscoveredInfo(nsIDNSServiceInfo* aDNSServiceInfo) - : mDNSServiceInfo(aDNSServiceInfo) -{ - nsCString tmp; - DebugOnly<nsresult> drv = aDNSServiceInfo->GetServiceName(tmp); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - CopyUTF8toUTF16(tmp, mService.mDisplayName); - - mService.mTransport = NS_LITERAL_STRING("mdns"); - - drv = aDNSServiceInfo->GetServiceType(tmp); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - CopyUTF8toUTF16(tmp, mService.mServiceType); - - nsCOMPtr<nsIPropertyBag2> attrs; - drv = aDNSServiceInfo->GetAttributes(getter_AddRefs(attrs)); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - if (attrs) { - attrs->GetPropertyAsAString(NS_LITERAL_STRING("cert"), mService.mCert); - attrs->GetPropertyAsAString(NS_LITERAL_STRING("path"), mService.mPath); - } - - // Construct a service id from the name, host, address, and port. - nsCString cHost; - drv = aDNSServiceInfo->GetHost(cHost); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - - nsCString cAddress; - drv = aDNSServiceInfo->GetAddress(cAddress); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - - uint16_t port; - drv = aDNSServiceInfo->GetPort(&port); - MOZ_ASSERT(NS_SUCCEEDED(drv)); - nsAutoString portStr; - portStr.AppendInt(port, 10); - - mService.mServiceId = - NS_ConvertUTF8toUTF16(cAddress) + - NS_LITERAL_STRING(":") + - portStr + - NS_LITERAL_STRING("|") + - mService.mServiceType + - NS_LITERAL_STRING("|") + - NS_ConvertUTF8toUTF16(cHost) + - NS_LITERAL_STRING("|") + - mService.mDisplayName; -} - - -nsresult -FlyWebMDNSService::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo, int32_t aErrorCode) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnResolveFailed"); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceRegistered"); - - nsCString cName; - if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) { - return NS_ERROR_FAILURE; - } - - nsString name = NS_ConvertUTF8toUTF16(cName); - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(name); - if (!existingServer) { - return NS_ERROR_FAILURE; - } - - existingServer->PublishedServerStarted(NS_OK); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnServiceUnregistered"); - - nsCString cName; - if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) { - return NS_ERROR_FAILURE; - } - - nsString name = NS_ConvertUTF8toUTF16(cName); - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(name); - if (!existingServer) { - return NS_ERROR_FAILURE; - } - - LOG_I("OnServiceRegistered(MDNS): De-advertised server with name %s.", cName.get()); - - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnRegistrationFailed"); - - nsCString cName; - if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) { - return NS_ERROR_FAILURE; - } - - nsString name = NS_ConvertUTF8toUTF16(cName); - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(name); - if (!existingServer) { - return NS_ERROR_FAILURE; - } - - LOG_I("OnServiceRegistered(MDNS): Registration of server with name %s failed.", cName.get()); - - // Remove the nsICancelable from the published server. - existingServer->PublishedServerStarted(NS_ERROR_FAILURE); - return NS_OK; -} - -nsresult -FlyWebMDNSService::OnUnregistrationFailed(nsIDNSServiceInfo* aServiceInfo, int32_t errorCode) -{ - LogDNSInfo(aServiceInfo, "FlyWebMDNSService::OnUnregistrationFailed"); - - nsCString cName; - if (NS_WARN_IF(NS_FAILED(aServiceInfo->GetServiceName(cName)))) { - return NS_ERROR_FAILURE; - } - - nsString name = NS_ConvertUTF8toUTF16(cName); - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(name); - if (!existingServer) { - return NS_ERROR_FAILURE; - } - - LOG_I("OnServiceRegistered(MDNS): Un-Advertisement of server with name %s failed.", cName.get()); - return NS_OK; -} - -nsresult -FlyWebMDNSService::Notify(nsITimer* timer) -{ - if (timer == mDiscoveryStopTimer.get()) { - LOG_I("MDNSService::Notify() got discovery stop timeout"); - // Internet discovery stop timer has fired. - nsresult rv = StopDiscovery(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; - } - - if (timer == mDiscoveryStartTimer.get()) { - LOG_I("MDNSService::Notify() got discovery start timeout"); - // Internet discovery start timer has fired. - nsresult rv = StartDiscovery(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; - } - - LOG_E("MDNSService::Notify got unknown timeout."); - return NS_OK; -} - -nsresult -FlyWebMDNSService::Init() -{ - MOZ_ASSERT(mDiscoveryState == DISCOVERY_IDLE); - - mDiscoveryStartTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (!mDiscoveryStartTimer) { - return NS_ERROR_FAILURE; - } - - mDiscoveryStopTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (!mDiscoveryStopTimer) { - return NS_ERROR_FAILURE; - } - - nsresult rv; - mDNSServiceDiscovery = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -FlyWebMDNSService::StartDiscovery() -{ - nsresult rv; - - // Always cancel the timer. - rv = mDiscoveryStartTimer->Cancel(); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_E("FlyWeb failed to cancel DNS service discovery start timer."); - } - - // If discovery is not idle, don't start it. - if (mDiscoveryState != DISCOVERY_IDLE) { - return NS_OK; - } - - LOG_I("FlyWeb starting dicovery."); - mDiscoveryState = DISCOVERY_STARTING; - - // start the discovery. - rv = mDNSServiceDiscovery->StartDiscovery(mServiceType, this, - getter_AddRefs(mCancelDiscovery)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_E("FlyWeb failed to start DNS service discovery."); - return rv; - } - - return NS_OK; -} - -nsresult -FlyWebMDNSService::StopDiscovery() -{ - nsresult rv; - - // Always cancel the timer. - rv = mDiscoveryStopTimer->Cancel(); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_E("FlyWeb failed to cancel DNS service discovery stop timer."); - } - - // If discovery is not running, do nothing. - if (mDiscoveryState != DISCOVERY_RUNNING) { - return NS_OK; - } - - LOG_I("FlyWeb stopping dicovery."); - - // Mark service discovery as stopping. - mDiscoveryState = DISCOVERY_STOPPING; - - if (mCancelDiscovery) { - LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery exists!"); - nsCOMPtr<nsICancelable> cancelDiscovery = mCancelDiscovery.forget(); - rv = cancelDiscovery->Cancel(NS_ERROR_ABORT); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_E("FlyWeb failed to cancel DNS stop service discovery."); - } - } else { - LOG_I("MDNSService::StopDiscovery() - mCancelDiscovery does not exist!"); - mDiscoveryState = DISCOVERY_IDLE; - } - - return NS_OK; -} - -void -FlyWebMDNSService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices) -{ - for (auto iter = mServiceMap.Iter(); !iter.Done(); iter.Next()) { - aServices.AppendElement(iter.UserData()->mService); - } -} - -bool -FlyWebMDNSService::HasService(const nsAString& aServiceId) -{ - return mServiceMap.Contains(aServiceId); -} - -nsresult -FlyWebMDNSService::PairWithService(const nsAString& aServiceId, - UniquePtr<FlyWebService::PairedInfo>& aInfo) -{ - MOZ_ASSERT(HasService(aServiceId)); - - nsresult rv; - nsCOMPtr<nsIUUIDGenerator> uuidgen = - do_GetService("@mozilla.org/uuid-generator;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsID id; - rv = uuidgen->GenerateUUIDInPlace(&id); - NS_ENSURE_SUCCESS(rv, rv); - - aInfo.reset(new FlyWebService::PairedInfo()); - - char uuidChars[NSID_LENGTH]; - id.ToProvidedString(uuidChars); - CopyUTF8toUTF16(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2), - aInfo->mService.mHostname); - - DiscoveredInfo* discInfo = mServiceMap.Get(aServiceId); - - nsAutoString url; - if (discInfo->mService.mCert.IsEmpty()) { - url.AssignLiteral("http://"); - } else { - url.AssignLiteral("https://"); - } - url.Append(aInfo->mService.mHostname + NS_LITERAL_STRING("/")); - nsCOMPtr<nsIURI> uiURL; - NS_NewURI(getter_AddRefs(uiURL), url); - MOZ_ASSERT(uiURL); - if (!discInfo->mService.mPath.IsEmpty()) { - nsCOMPtr<nsIURI> tmp = uiURL.forget(); - NS_NewURI(getter_AddRefs(uiURL), discInfo->mService.mPath, nullptr, tmp); - } - if (uiURL) { - nsAutoCString spec; - uiURL->GetSpec(spec); - CopyUTF8toUTF16(spec, aInfo->mService.mUiUrl); - } - - aInfo->mService.mDiscoveredService = discInfo->mService; - aInfo->mDNSServiceInfo = discInfo->mDNSServiceInfo; - - return NS_OK; -} - -nsresult -FlyWebMDNSService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer) -{ - - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(aServer->Name()); - MOZ_ASSERT(existingServer); - - // Advertise the service via mdns. - RefPtr<net::nsDNSServiceInfo> serviceInfo(new net::nsDNSServiceInfo()); - - serviceInfo->SetPort(aServer->Port()); - serviceInfo->SetServiceType(mServiceType); - - nsCString certKey; - aServer->GetCertKey(certKey); - nsString uiURL; - aServer->GetUiUrl(uiURL); - - if (!uiURL.IsEmpty() || !certKey.IsEmpty()) { - RefPtr<nsHashPropertyBag> attrs = new nsHashPropertyBag(); - if (!uiURL.IsEmpty()) { - attrs->SetPropertyAsAString(NS_LITERAL_STRING("path"), uiURL); - } - if (!certKey.IsEmpty()) { - attrs->SetPropertyAsACString(NS_LITERAL_STRING("cert"), certKey); - } - serviceInfo->SetAttributes(attrs); - } - - nsCString cstrName = NS_ConvertUTF16toUTF8(aServer->Name()); - LOG_I("MDNSService::StartDiscoveryOf() advertising service %s", cstrName.get()); - serviceInfo->SetServiceName(cstrName); - - LogDNSInfo(serviceInfo, "FlyWebMDNSService::StartDiscoveryOf"); - - // Advertise the service. - nsCOMPtr<nsICancelable> cancelRegister; - nsresult rv = mDNSServiceDiscovery-> - RegisterService(serviceInfo, this, getter_AddRefs(cancelRegister)); - NS_ENSURE_SUCCESS(rv, rv); - - // All done. - aServer->SetCancelRegister(cancelRegister); - - return NS_OK; -} - -void -FlyWebMDNSService::EnsureDiscoveryStarted() -{ - mDiscoveryActive = true; - // If state is idle, start discovery immediately. - if (mDiscoveryState == DISCOVERY_IDLE) { - StartDiscovery(); - } -} - -void -FlyWebMDNSService::EnsureDiscoveryStopped() -{ - // All we need to do is set the flag to false. - // If current state is IDLE, it's already the correct state. - // Otherwise, the handlers for the internal state - // transitions will check this flag and drive the state - // towards IDLE. - mDiscoveryActive = false; -} - -static StaticRefPtr<FlyWebService> gFlyWebService; - -NS_IMPL_ISUPPORTS(FlyWebService, nsIObserver) - -FlyWebService::FlyWebService() - : mMonitor("FlyWebService::mMonitor") -{ - MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, "inner-window-destroyed", false); - } -} - -FlyWebService::~FlyWebService() -{ -} - -FlyWebService* -FlyWebService::GetExisting() -{ - return gFlyWebService; -} - -FlyWebService* -FlyWebService::GetOrCreate() -{ - if (!gFlyWebService) { - gFlyWebService = new FlyWebService(); - ClearOnShutdown(&gFlyWebService); - ErrorResult rv = gFlyWebService->Init(); - if (rv.Failed()) { - gFlyWebService = nullptr; - return nullptr; - } - } - return gFlyWebService; -} - -ErrorResult -FlyWebService::Init() -{ - // Most functions of FlyWebService should not be started in the child. - // Instead FlyWebService in the child is mainly responsible for tracking - // publishedServer lifetimes. Other functions are handled by the - // FlyWebService running in the parent. - if (XRE_GetProcessType() == GeckoProcessType_Content) { - return ErrorResult(NS_OK); - } - - MOZ_ASSERT(NS_IsMainThread()); - if (!mMDNSHttpService) { - mMDNSHttpService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_http._tcp.")); - ErrorResult rv; - - rv = mMDNSHttpService->Init(); - if (rv.Failed()) { - LOG_E("FlyWebService failed to initialize MDNS _http._tcp."); - mMDNSHttpService = nullptr; - rv.SuppressException(); - } - } - - if (!mMDNSFlywebService) { - mMDNSFlywebService = new FlyWebMDNSService(this, NS_LITERAL_CSTRING("_flyweb._tcp.")); - ErrorResult rv; - - rv = mMDNSFlywebService->Init(); - if (rv.Failed()) { - LOG_E("FlyWebService failed to initialize MDNS _flyweb._tcp."); - mMDNSFlywebService = nullptr; - rv.SuppressException(); - } - } - - return ErrorResult(NS_OK); -} - -static already_AddRefed<FlyWebPublishPromise> -MakeRejectionPromise(const char* name) -{ - MozPromiseHolder<FlyWebPublishPromise> holder; - RefPtr<FlyWebPublishPromise> promise = holder.Ensure(name); - holder.Reject(NS_ERROR_FAILURE, name); - return promise.forget(); -} - -static bool -CheckForFlyWebAddon(const nsACString& uriString) -{ - // Before proceeding, ensure that the FlyWeb system addon exists. - nsresult rv; - nsCOMPtr<nsIURI> uri; - rv = NS_NewURI(getter_AddRefs(uri), uriString); - if (NS_FAILED(rv)) { - return false; - } - - JSAddonId *addonId = MapURIToAddonID(uri); - if (!addonId) { - return false; - } - - JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId)); - nsAutoString addonIdString; - AssignJSFlatString(addonIdString, flat); - if (!addonIdString.EqualsLiteral("flyweb@mozilla.org")) { - nsCString addonIdCString = NS_ConvertUTF16toUTF8(addonIdString); - return false; - } - - return true; -} - -already_AddRefed<FlyWebPublishPromise> -FlyWebService::PublishServer(const nsAString& aName, - const FlyWebPublishOptions& aOptions, - nsPIDOMWindowInner* aWindow) -{ - // Scan uiUrl for illegal characters - - RefPtr<FlyWebPublishedServer> existingServer = - FlyWebService::GetOrCreate()->FindPublishedServerByName(aName); - if (existingServer) { - LOG_I("PublishServer: Trying to publish server with already-existing name %s.", - NS_ConvertUTF16toUTF8(aName).get()); - return MakeRejectionPromise(__func__); - } - - RefPtr<FlyWebPublishedServer> server; - if (XRE_GetProcessType() == GeckoProcessType_Content) { - server = new FlyWebPublishedServerChild(aWindow, aName, aOptions); - } else { - server = new FlyWebPublishedServerImpl(aWindow, aName, aOptions); - - // Before proceeding, ensure that the FlyWeb system addon exists. - if (!CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/skin/icon-64.png")) && - !CheckForFlyWebAddon(NS_LITERAL_CSTRING("chrome://flyweb/content/icon-64.png"))) - { - LOG_E("PublishServer: Failed to find FlyWeb system addon."); - return MakeRejectionPromise(__func__); - } - } - - if (aWindow) { - nsresult rv; - - MOZ_ASSERT(NS_IsMainThread()); - rv = NS_DispatchToCurrentThread( - MakeAndAddRef<FlyWebPublishServerPermissionCheck>( - NS_ConvertUTF16toUTF8(aName), aWindow->WindowID(), server)); - if (NS_WARN_IF(NS_FAILED(rv))) { - LOG_E("PublishServer: Failed to dispatch permission check runnable for %s", - NS_ConvertUTF16toUTF8(aName).get()); - return MakeRejectionPromise(__func__); - } - } else { - // If aWindow is null, we're definitely in the e10s parent process. - // In this case, we know that permission has already been granted - // by the user because of content-process prompt. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - server->PermissionGranted(true); - } - - mServers.AppendElement(server); - - return server->GetPublishPromise(); -} - -already_AddRefed<FlyWebPublishedServer> -FlyWebService::FindPublishedServerByName( - const nsAString& aName) -{ - MOZ_ASSERT(NS_IsMainThread()); - for (FlyWebPublishedServer* publishedServer : mServers) { - if (publishedServer->Name().Equals(aName)) { - RefPtr<FlyWebPublishedServer> server = publishedServer; - return server.forget(); - } - } - return nullptr; -} - -void -FlyWebService::RegisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager) -{ - MOZ_ASSERT(NS_IsMainThread()); - mDiscoveryManagerTable.PutEntry(aDiscoveryManager); - if (mMDNSHttpService) { - mMDNSHttpService->EnsureDiscoveryStarted(); - } - if (mMDNSFlywebService) { - mMDNSFlywebService->EnsureDiscoveryStarted(); - } -} - -void -FlyWebService::UnregisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager) -{ - MOZ_ASSERT(NS_IsMainThread()); - mDiscoveryManagerTable.RemoveEntry(aDiscoveryManager); - if (mDiscoveryManagerTable.IsEmpty()) { - if (mMDNSHttpService) { - mMDNSHttpService->EnsureDiscoveryStopped(); - } - if (mMDNSFlywebService) { - mMDNSFlywebService->EnsureDiscoveryStopped(); - } - } -} - -NS_IMETHODIMP -FlyWebService::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (strcmp(aTopic, "inner-window-destroyed")) { - return NS_OK; - } - - nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); - NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); - - uint64_t innerID; - nsresult rv = wrapper->GetData(&innerID); - NS_ENSURE_SUCCESS(rv, rv); - - for (FlyWebPublishedServer* server : mServers) { - if (server->OwnerWindowID() == innerID) { - server->Close(); - } - } - - return NS_OK; -} - -void -FlyWebService::UnregisterServer(FlyWebPublishedServer* aServer) -{ - MOZ_ASSERT(NS_IsMainThread()); - DebugOnly<bool> removed = mServers.RemoveElement(aServer); - MOZ_ASSERT(removed); -} - -bool -FlyWebService::HasConnectionOrServer(uint64_t aWindowID) -{ - MOZ_ASSERT(NS_IsMainThread()); - for (FlyWebPublishedServer* server : mServers) { - nsPIDOMWindowInner* win = server->GetOwner(); - if (win && win->WindowID() == aWindowID) { - return true; - } - } - - return false; -} - -void -FlyWebService::NotifyDiscoveredServicesChanged() -{ - // Process the service map, add to the pair map. - for (auto iter = mDiscoveryManagerTable.Iter(); !iter.Done(); iter.Next()) { - iter.Get()->GetKey()->NotifyDiscoveredServicesChanged(); - } -} - -void -FlyWebService::ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (mMDNSHttpService) { - mMDNSHttpService->ListDiscoveredServices(aServices); - } - if (mMDNSFlywebService) { - mMDNSFlywebService->ListDiscoveredServices(aServices); - } -} - -void -FlyWebService::PairWithService(const nsAString& aServiceId, - FlyWebPairingCallback& aCallback) -{ - MOZ_ASSERT(NS_IsMainThread()); - // See if we have already paired with this service. If so, re-use the - // FlyWebPairedService for that. - { - ReentrantMonitorAutoEnter pairedMapLock(mMonitor); - for (auto iter = mPairedServiceTable.Iter(); !iter.Done(); iter.Next()) { - PairedInfo* pairInfo = iter.UserData(); - if (pairInfo->mService.mDiscoveredService.mServiceId.Equals(aServiceId)) { - ErrorResult er; - ReentrantMonitorAutoExit pairedMapRelease(mMonitor); - aCallback.PairingSucceeded(pairInfo->mService, er); - ENSURE_SUCCESS_VOID(er); - return; - } - } - } - - UniquePtr<PairedInfo> pairInfo; - - nsresult rv = NS_OK; - bool notFound = false; - if (mMDNSHttpService && mMDNSHttpService->HasService(aServiceId)) { - rv = mMDNSHttpService->PairWithService(aServiceId, pairInfo); - } else if (mMDNSFlywebService && mMDNSFlywebService->HasService(aServiceId)) { - rv = mMDNSFlywebService->PairWithService(aServiceId, pairInfo); - } else { - notFound = true; - } - - if (NS_FAILED(rv)) { - ErrorResult result; - result.Throw(rv); - const nsAString& reason = NS_LITERAL_STRING("Error pairing."); - aCallback.PairingFailed(reason, result); - ENSURE_SUCCESS_VOID(result); - return; - } - - if (!pairInfo) { - ErrorResult res; - const nsAString& reason = notFound ? - NS_LITERAL_STRING("No such service.") : - NS_LITERAL_STRING("Error pairing."); - aCallback.PairingFailed(reason, res); - ENSURE_SUCCESS_VOID(res); - return; - } - - // Add fingerprint to certificate override database. - if (!pairInfo->mService.mDiscoveredService.mCert.IsEmpty()) { - nsCOMPtr<nsICertOverrideService> override = - do_GetService("@mozilla.org/security/certoverride;1"); - if (!override || - NS_FAILED(override->RememberTemporaryValidityOverrideUsingFingerprint( - NS_ConvertUTF16toUTF8(pairInfo->mService.mHostname), - -1, - NS_ConvertUTF16toUTF8(pairInfo->mService.mDiscoveredService.mCert), - nsICertOverrideService::ERROR_UNTRUSTED | - nsICertOverrideService::ERROR_MISMATCH))) { - ErrorResult res; - aCallback.PairingFailed(NS_LITERAL_STRING("Error adding certificate override."), res); - ENSURE_SUCCESS_VOID(res); - return; - } - } - - // Grab a weak reference to the PairedInfo so that we can - // use it even after ownership has been transferred to mPairedServiceTable - PairedInfo* pairInfoWeak = pairInfo.release(); - - { - ReentrantMonitorAutoEnter pairedMapLock(mMonitor); - mPairedServiceTable.Put( - NS_ConvertUTF16toUTF8(pairInfoWeak->mService.mHostname), pairInfoWeak); - } - - ErrorResult er; - aCallback.PairingSucceeded(pairInfoWeak->mService, er); - ENSURE_SUCCESS_VOID(er); -} - -nsresult -FlyWebService::CreateTransportForHost(const char **types, - uint32_t typeCount, - const nsACString &host, - int32_t port, - const nsACString &hostRoute, - int32_t portRoute, - nsIProxyInfo *proxyInfo, - nsISocketTransport **result) -{ - // This might be called on background threads - - *result = nullptr; - - nsCString ipAddrString; - uint16_t discPort; - - { - ReentrantMonitorAutoEnter pairedMapLock(mMonitor); - - PairedInfo* info = mPairedServiceTable.Get(host); - - if (!info) { - return NS_OK; - } - - // Get the ip address of the underlying service. - info->mDNSServiceInfo->GetAddress(ipAddrString); - info->mDNSServiceInfo->GetPort(&discPort); - } - - // Parse it into an NetAddr. - PRNetAddr prNetAddr; - PRStatus status = PR_StringToNetAddr(ipAddrString.get(), &prNetAddr); - NS_ENSURE_FALSE(status == PR_FAILURE, NS_ERROR_FAILURE); - - // Convert PRNetAddr to NetAddr. - mozilla::net::NetAddr netAddr; - PRNetAddrToNetAddr(&prNetAddr, &netAddr); - netAddr.inet.port = htons(discPort); - - RefPtr<mozilla::net::nsSocketTransport> trans = new mozilla::net::nsSocketTransport(); - nsresult rv = trans->InitPreResolved( - types, typeCount, host, port, hostRoute, portRoute, proxyInfo, &netAddr); - NS_ENSURE_SUCCESS(rv, rv); - - trans.forget(result); - return NS_OK; -} - -void -FlyWebService::StartDiscoveryOf(FlyWebPublishedServerImpl* aServer) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsresult rv = mMDNSFlywebService ? - mMDNSFlywebService->StartDiscoveryOf(aServer) : - NS_ERROR_FAILURE; - - if (NS_FAILED(rv)) { - aServer->PublishedServerStarted(rv); - } -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/flyweb/FlyWebService.h b/dom/flyweb/FlyWebService.h deleted file mode 100644 index f7b983440..000000000 --- a/dom/flyweb/FlyWebService.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- 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_FlyWebService_h -#define mozilla_dom_FlyWebService_h - -#include "nsISupportsImpl.h" -#include "mozilla/ErrorResult.h" -#include "nsIProtocolHandler.h" -#include "nsDataHashtable.h" -#include "nsClassHashtable.h" -#include "nsIObserver.h" -#include "mozilla/MozPromise.h" -#include "mozilla/ReentrantMonitor.h" -#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h" -#include "nsITimer.h" -#include "nsICancelable.h" -#include "nsIDNSServiceDiscovery.h" - -class nsPIDOMWindowInner; -class nsIProxyInfo; -class nsISocketTransport; - -namespace mozilla { -namespace dom { - -struct FlyWebPublishOptions; -struct FlyWebFilter; -class FlyWebPublishedServer; -class FlyWebPublishedServerImpl; -class FlyWebPairingCallback; -class FlyWebDiscoveryManager; -class FlyWebMDNSService; - -typedef MozPromise<RefPtr<FlyWebPublishedServer>, nsresult, false> - FlyWebPublishPromise; - -class FlyWebService final : public nsIObserver -{ - friend class FlyWebMDNSService; -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIOBSERVER - - static FlyWebService* GetExisting(); - static FlyWebService* GetOrCreate(); - static already_AddRefed<FlyWebService> GetOrCreateAddRefed() - { - return do_AddRef(GetOrCreate()); - } - - already_AddRefed<FlyWebPublishPromise> - PublishServer(const nsAString& aName, - const FlyWebPublishOptions& aOptions, - nsPIDOMWindowInner* aWindow); - - void UnregisterServer(FlyWebPublishedServer* aServer); - - bool HasConnectionOrServer(uint64_t aWindowID); - - void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices); - void PairWithService(const nsAString& aServiceId, FlyWebPairingCallback& aCallback); - nsresult CreateTransportForHost(const char **types, - uint32_t typeCount, - const nsACString &host, - int32_t port, - const nsACString &hostRoute, - int32_t portRoute, - nsIProxyInfo *proxyInfo, - nsISocketTransport **result); - - already_AddRefed<FlyWebPublishedServer> FindPublishedServerByName( - const nsAString& aName); - - void RegisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager); - void UnregisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager); - - // Should only be called by FlyWebPublishedServerImpl - void StartDiscoveryOf(FlyWebPublishedServerImpl* aServer); - -private: - FlyWebService(); - ~FlyWebService(); - - ErrorResult Init(); - - void NotifyDiscoveredServicesChanged(); - - // Might want to make these hashes for perf - nsTArray<FlyWebPublishedServer*> mServers; - - RefPtr<FlyWebMDNSService> mMDNSHttpService; - RefPtr<FlyWebMDNSService> mMDNSFlywebService; - - struct PairedInfo - { - FlyWebPairedService mService; - nsCOMPtr<nsIDNSServiceInfo> mDNSServiceInfo; - }; - nsClassHashtable<nsCStringHashKey, PairedInfo> - mPairedServiceTable; - ReentrantMonitor mMonitor; // Protecting mPairedServiceTable - - nsTHashtable<nsPtrHashKey<FlyWebDiscoveryManager>> mDiscoveryManagerTable; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FlyWebService_h diff --git a/dom/flyweb/HttpServer.cpp b/dom/flyweb/HttpServer.cpp deleted file mode 100644 index 26e15d9d5..000000000 --- a/dom/flyweb/HttpServer.cpp +++ /dev/null @@ -1,1319 +0,0 @@ -/* -*- 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 "mozilla/dom/HttpServer.h" -#include "nsISocketTransport.h" -#include "nsWhitespaceTokenizer.h" -#include "nsNetUtil.h" -#include "nsIStreamTransportService.h" -#include "nsIAsyncStreamCopier2.h" -#include "nsIPipe.h" -#include "nsIOService.h" -#include "nsIHttpChannelInternal.h" -#include "Base64.h" -#include "WebSocketChannel.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsIX509Cert.h" - -static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); - -namespace mozilla { -namespace dom { - -static LazyLogModule gHttpServerLog("HttpServer"); -#undef LOG_I -#define LOG_I(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#undef LOG_V -#define LOG_V(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Verbose, (__VA_ARGS__)) -#undef LOG_E -#define LOG_E(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Error, (__VA_ARGS__)) - - -NS_IMPL_ISUPPORTS(HttpServer, - nsIServerSocketListener, - nsILocalCertGetCallback) - -HttpServer::HttpServer() - : mPort() - , mHttps() -{ -} - -HttpServer::~HttpServer() -{ -} - -void -HttpServer::Init(int32_t aPort, bool aHttps, HttpServerListener* aListener) -{ - mPort = aPort; - mHttps = aHttps; - mListener = aListener; - - if (mHttps) { - nsCOMPtr<nsILocalCertService> lcs = - do_CreateInstance("@mozilla.org/security/local-cert-service;1"); - nsresult rv = lcs->GetOrCreateCert(NS_LITERAL_CSTRING("flyweb"), this); - if (NS_FAILED(rv)) { - NotifyStarted(rv); - } - } else { - // Make sure to always have an async step before notifying callbacks - HandleCert(nullptr, NS_OK); - } -} - -NS_IMETHODIMP -HttpServer::HandleCert(nsIX509Cert* aCert, nsresult aResult) -{ - nsresult rv = aResult; - if (NS_SUCCEEDED(rv)) { - rv = StartServerSocket(aCert); - } - - if (NS_FAILED(rv) && mServerSocket) { - mServerSocket->Close(); - mServerSocket = nullptr; - } - - NotifyStarted(rv); - - return NS_OK; -} - -void -HttpServer::NotifyStarted(nsresult aStatus) -{ - RefPtr<HttpServerListener> listener = mListener; - nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction([listener, aStatus] () - { - listener->OnServerStarted(aStatus); - }); - NS_DispatchToCurrentThread(event); -} - -nsresult -HttpServer::StartServerSocket(nsIX509Cert* aCert) -{ - nsresult rv; - mServerSocket = - do_CreateInstance(aCert ? "@mozilla.org/network/tls-server-socket;1" - : "@mozilla.org/network/server-socket;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mServerSocket->Init(mPort, false, -1); - NS_ENSURE_SUCCESS(rv, rv); - - if (aCert) { - nsCOMPtr<nsITLSServerSocket> tls = do_QueryInterface(mServerSocket); - rv = tls->SetServerCert(aCert); - NS_ENSURE_SUCCESS(rv, rv); - - rv = tls->SetSessionTickets(false); - NS_ENSURE_SUCCESS(rv, rv); - - mCert = aCert; - } - - rv = mServerSocket->AsyncListen(this); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mServerSocket->GetPort(&mPort); - NS_ENSURE_SUCCESS(rv, rv); - - LOG_I("HttpServer::StartServerSocket(%p)", this); - - return NS_OK; -} - -NS_IMETHODIMP -HttpServer::OnSocketAccepted(nsIServerSocket* aServ, - nsISocketTransport* aTransport) -{ - MOZ_ASSERT(SameCOMIdentity(aServ, mServerSocket)); - - nsresult rv; - RefPtr<Connection> conn = new Connection(aTransport, this, rv); - NS_ENSURE_SUCCESS(rv, rv); - - LOG_I("HttpServer::OnSocketAccepted(%p) - Socket %p", this, conn.get()); - - mConnections.AppendElement(conn.forget()); - - return NS_OK; -} - -NS_IMETHODIMP -HttpServer::OnStopListening(nsIServerSocket* aServ, - nsresult aStatus) -{ - MOZ_ASSERT(aServ == mServerSocket || !mServerSocket); - - LOG_I("HttpServer::OnStopListening(%p) - status 0x%lx", this, aStatus); - - Close(); - - return NS_OK; -} - -void -HttpServer::SendResponse(InternalRequest* aRequest, InternalResponse* aResponse) -{ - for (Connection* conn : mConnections) { - if (conn->TryHandleResponse(aRequest, aResponse)) { - return; - } - } - - MOZ_ASSERT(false, "Unknown request"); -} - -already_AddRefed<nsITransportProvider> -HttpServer::AcceptWebSocket(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - for (Connection* conn : mConnections) { - if (!conn->HasPendingWebSocketRequest(aConnectRequest)) { - continue; - } - nsCOMPtr<nsITransportProvider> provider = - conn->HandleAcceptWebSocket(aProtocol, aRv); - if (aRv.Failed()) { - conn->Close(); - } - // This connection is now owned by the websocket, or we just closed it - mConnections.RemoveElement(conn); - return provider.forget(); - } - - aRv.Throw(NS_ERROR_UNEXPECTED); - MOZ_ASSERT(false, "Unknown request"); - - return nullptr; -} - -void -HttpServer::SendWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse) -{ - for (Connection* conn : mConnections) { - if (conn->HasPendingWebSocketRequest(aConnectRequest)) { - conn->HandleWebSocketResponse(aResponse); - return; - } - } - - MOZ_ASSERT(false, "Unknown request"); -} - -void -HttpServer::Close() -{ - if (mServerSocket) { - mServerSocket->Close(); - mServerSocket = nullptr; - } - - if (mListener) { - RefPtr<HttpServerListener> listener = mListener.forget(); - listener->OnServerClose(); - } - - for (Connection* conn : mConnections) { - conn->Close(); - } - mConnections.Clear(); -} - -void -HttpServer::GetCertKey(nsACString& aKey) -{ - nsAutoString tmp; - if (mCert) { - mCert->GetSha256Fingerprint(tmp); - } - LossyCopyUTF16toASCII(tmp, aKey); -} - -NS_IMPL_ISUPPORTS(HttpServer::TransportProvider, - nsITransportProvider) - -HttpServer::TransportProvider::~TransportProvider() -{ -} - -NS_IMETHODIMP -HttpServer::TransportProvider::SetListener(nsIHttpUpgradeListener* aListener) -{ - MOZ_ASSERT(!mListener); - MOZ_ASSERT(aListener); - - mListener = aListener; - - MaybeNotify(); - - return NS_OK; -} - -NS_IMETHODIMP -HttpServer::TransportProvider::GetIPCChild(PTransportProviderChild** aChild) -{ - MOZ_CRASH("Don't call this in parent process"); - *aChild = nullptr; - return NS_OK; -} - -void -HttpServer::TransportProvider::SetTransport(nsISocketTransport* aTransport, - nsIAsyncInputStream* aInput, - nsIAsyncOutputStream* aOutput) -{ - MOZ_ASSERT(!mTransport); - MOZ_ASSERT(aTransport && aInput && aOutput); - - mTransport = aTransport; - mInput = aInput; - mOutput = aOutput; - - MaybeNotify(); -} - -void -HttpServer::TransportProvider::MaybeNotify() -{ - if (mTransport && mListener) { - RefPtr<TransportProvider> self = this; - nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction([self, this] () - { - mListener->OnTransportAvailable(mTransport, mInput, mOutput); - }); - NS_DispatchToCurrentThread(event); - } -} - -NS_IMPL_ISUPPORTS(HttpServer::Connection, - nsIInputStreamCallback, - nsIOutputStreamCallback) - -HttpServer::Connection::Connection(nsISocketTransport* aTransport, - HttpServer* aServer, - nsresult& rv) - : mServer(aServer) - , mTransport(aTransport) - , mState(eRequestLine) - , mPendingReqVersion() - , mRemainingBodySize() - , mCloseAfterRequest(false) -{ - nsCOMPtr<nsIInputStream> input; - rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(input)); - NS_ENSURE_SUCCESS_VOID(rv); - - mInput = do_QueryInterface(input); - - nsCOMPtr<nsIOutputStream> output; - rv = mTransport->OpenOutputStream(0, 0, 0, getter_AddRefs(output)); - NS_ENSURE_SUCCESS_VOID(rv); - - mOutput = do_QueryInterface(output); - - if (mServer->mHttps) { - SetSecurityObserver(true); - } else { - mInput->AsyncWait(this, 0, 0, NS_GetCurrentThread()); - } -} - -NS_IMETHODIMP -HttpServer::Connection::OnHandshakeDone(nsITLSServerSocket* aServer, - nsITLSClientStatus* aStatus) -{ - LOG_I("HttpServer::Connection::OnHandshakeDone(%p)", this); - - // XXX Verify connection security - - SetSecurityObserver(false); - mInput->AsyncWait(this, 0, 0, NS_GetCurrentThread()); - - return NS_OK; -} - -void -HttpServer::Connection::SetSecurityObserver(bool aListen) -{ - LOG_I("HttpServer::Connection::SetSecurityObserver(%p) - %s", this, - aListen ? "On" : "Off"); - - nsCOMPtr<nsISupports> secInfo; - mTransport->GetSecurityInfo(getter_AddRefs(secInfo)); - nsCOMPtr<nsITLSServerConnectionInfo> tlsConnInfo = - do_QueryInterface(secInfo); - MOZ_ASSERT(tlsConnInfo); - tlsConnInfo->SetSecurityObserver(aListen ? this : nullptr); -} - -HttpServer::Connection::~Connection() -{ -} - -NS_IMETHODIMP -HttpServer::Connection::OnInputStreamReady(nsIAsyncInputStream* aStream) -{ - MOZ_ASSERT(!mInput || aStream == mInput); - - LOG_I("HttpServer::Connection::OnInputStreamReady(%p)", this); - - if (!mInput || mState == ePause) { - return NS_OK; - } - - uint64_t avail; - nsresult rv = mInput->Available(&avail); - if (NS_FAILED(rv)) { - LOG_I("HttpServer::Connection::OnInputStreamReady(%p) - Connection closed", this); - - mServer->mConnections.RemoveElement(this); - // Connection closed. Handle errors here. - return NS_OK; - } - - uint32_t numRead; - rv = mInput->ReadSegments(ReadSegmentsFunc, - this, - UINT32_MAX, - &numRead); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInput->AsyncWait(this, 0, 0, NS_GetCurrentThread()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HttpServer::Connection::ReadSegmentsFunc(nsIInputStream* aIn, - void* aClosure, - const char* aBuffer, - uint32_t aToOffset, - uint32_t aCount, - uint32_t* aWriteCount) -{ - const char* buffer = aBuffer; - nsresult rv = static_cast<HttpServer::Connection*>(aClosure)-> - ConsumeInput(buffer, buffer + aCount); - - *aWriteCount = buffer - aBuffer; - MOZ_ASSERT(*aWriteCount <= aCount); - - return rv; -} - -static const char* -findCRLF(const char* aBuffer, const char* aEnd) -{ - if (aBuffer + 1 >= aEnd) { - return nullptr; - } - - const char* pos; - while ((pos = static_cast<const char*>(memchr(aBuffer, - '\r', - aEnd - aBuffer - 1)))) { - if (*(pos + 1) == '\n') { - return pos; - } - aBuffer = pos + 1; - } - return nullptr; -} - -nsresult -HttpServer::Connection::ConsumeInput(const char*& aBuffer, - const char* aEnd) -{ - nsresult rv; - while (mState == eRequestLine || - mState == eHeaders) { - // Consume line-by-line - - // Check if buffer boundry ended up right between the CR and LF - if (!mInputBuffer.IsEmpty() && mInputBuffer.Last() == '\r' && - *aBuffer == '\n') { - aBuffer++; - rv = ConsumeLine(mInputBuffer.BeginReading(), mInputBuffer.Length() - 1); - NS_ENSURE_SUCCESS(rv, rv); - - mInputBuffer.Truncate(); - } - - // Look for a CRLF - const char* pos = findCRLF(aBuffer, aEnd); - if (!pos) { - mInputBuffer.Append(aBuffer, aEnd - aBuffer); - aBuffer = aEnd; - return NS_OK; - } - - if (!mInputBuffer.IsEmpty()) { - mInputBuffer.Append(aBuffer, pos - aBuffer); - aBuffer = pos + 2; - rv = ConsumeLine(mInputBuffer.BeginReading(), mInputBuffer.Length() - 1); - NS_ENSURE_SUCCESS(rv, rv); - - mInputBuffer.Truncate(); - } else { - rv = ConsumeLine(aBuffer, pos - aBuffer); - NS_ENSURE_SUCCESS(rv, rv); - - aBuffer = pos + 2; - } - } - - if (mState == eBody) { - uint32_t size = std::min(mRemainingBodySize, - static_cast<uint32_t>(aEnd - aBuffer)); - uint32_t written = size; - - if (mCurrentRequestBody) { - rv = mCurrentRequestBody->Write(aBuffer, size, &written); - // Since we've given the pipe unlimited size, we should never - // end up needing to block. - MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK); - if (NS_FAILED(rv)) { - written = size; - mCurrentRequestBody = nullptr; - } - } - - aBuffer += written; - mRemainingBodySize -= written; - if (!mRemainingBodySize) { - mCurrentRequestBody->Close(); - mCurrentRequestBody = nullptr; - mState = eRequestLine; - } - } - - return NS_OK; -} - -bool -ContainsToken(const nsCString& aList, const nsCString& aToken) -{ - nsCCharSeparatedTokenizer tokens(aList, ','); - bool found = false; - while (!found && tokens.hasMoreTokens()) { - found = tokens.nextToken().Equals(aToken); - } - return found; -} - -static bool -IsWebSocketRequest(InternalRequest* aRequest, uint32_t aHttpVersion) -{ - if (aHttpVersion < 1) { - return false; - } - - nsAutoCString str; - aRequest->GetMethod(str); - if (!str.EqualsLiteral("GET")) { - return false; - } - - InternalHeaders* headers = aRequest->Headers(); - ErrorResult res; - - headers->GetFirst(NS_LITERAL_CSTRING("upgrade"), str, res); - MOZ_ASSERT(!res.Failed()); - if (!str.EqualsLiteral("websocket")) { - return false; - } - - headers->GetFirst(NS_LITERAL_CSTRING("connection"), str, res); - MOZ_ASSERT(!res.Failed()); - if (!ContainsToken(str, NS_LITERAL_CSTRING("Upgrade"))) { - return false; - } - - headers->GetFirst(NS_LITERAL_CSTRING("sec-websocket-key"), str, res); - MOZ_ASSERT(!res.Failed()); - nsAutoCString binary; - if (NS_FAILED(Base64Decode(str, binary)) || binary.Length() != 16) { - return false; - } - - nsresult rv; - headers->GetFirst(NS_LITERAL_CSTRING("sec-websocket-version"), str, res); - MOZ_ASSERT(!res.Failed()); - if (str.ToInteger(&rv) != 13 || NS_FAILED(rv)) { - return false; - } - - return true; -} - -nsresult -HttpServer::Connection::ConsumeLine(const char* aBuffer, - size_t aLength) -{ - MOZ_ASSERT(mState == eRequestLine || - mState == eHeaders); - - if (MOZ_LOG_TEST(gHttpServerLog, mozilla::LogLevel::Verbose)) { - nsCString line(aBuffer, aLength); - LOG_V("HttpServer::Connection::ConsumeLine(%p) - \"%s\"", this, line.get()); - } - - if (mState == eRequestLine) { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsing request line", this); - NS_ENSURE_FALSE(mCloseAfterRequest, NS_ERROR_UNEXPECTED); - - if (aLength == 0) { - // Ignore empty lines before the request line - return NS_OK; - } - MOZ_ASSERT(!mPendingReq); - - // Process request line - nsCWhitespaceTokenizer tokens(Substring(aBuffer, aLength)); - - NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); - nsDependentCSubstring method = tokens.nextToken(); - NS_ENSURE_TRUE(NS_IsValidHTTPToken(method), NS_ERROR_UNEXPECTED); - NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); - nsDependentCSubstring url = tokens.nextToken(); - // Seems like it's also allowed to pass full urls with scheme+host+port. - // May need to support that. - NS_ENSURE_TRUE(url.First() == '/', NS_ERROR_UNEXPECTED); - mPendingReq = new InternalRequest(url, /* aURLFragment */ EmptyCString()); - mPendingReq->SetMethod(method); - NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); - nsDependentCSubstring version = tokens.nextToken(); - NS_ENSURE_TRUE(StringBeginsWith(version, NS_LITERAL_CSTRING("HTTP/1.")), - NS_ERROR_UNEXPECTED); - nsresult rv; - // This integer parsing is likely not strict enough. - nsCString reqVersion; - reqVersion = Substring(version, MOZ_ARRAY_LENGTH("HTTP/1.") - 1); - mPendingReqVersion = reqVersion.ToInteger(&rv); - NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); - - NS_ENSURE_FALSE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED); - - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsed request line", this); - - mState = eHeaders; - - return NS_OK; - } - - if (aLength == 0) { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Found end of headers", this); - - MaybeAddPendingHeader(); - - ErrorResult res; - mPendingReq->Headers()->SetGuard(HeadersGuardEnum::Immutable, res); - - // Check for WebSocket - if (IsWebSocketRequest(mPendingReq, mPendingReqVersion)) { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Fire OnWebSocket", this); - - mState = ePause; - mPendingWebSocketRequest = mPendingReq.forget(); - mPendingReqVersion = 0; - - RefPtr<HttpServerListener> listener = mServer->mListener; - RefPtr<InternalRequest> request = mPendingWebSocketRequest; - nsCOMPtr<nsIRunnable> event = - NS_NewRunnableFunction([listener, request] () - { - listener->OnWebSocket(request); - }); - NS_DispatchToCurrentThread(event); - - return NS_OK; - } - - nsAutoCString header; - mPendingReq->Headers()->GetFirst(NS_LITERAL_CSTRING("connection"), - header, - res); - MOZ_ASSERT(!res.Failed()); - // 1.0 defaults to closing connections. - // 1.1 and higher defaults to keep-alive. - if (ContainsToken(header, NS_LITERAL_CSTRING("close")) || - (mPendingReqVersion == 0 && - !ContainsToken(header, NS_LITERAL_CSTRING("keep-alive")))) { - mCloseAfterRequest = true; - } - - mPendingReq->Headers()->GetFirst(NS_LITERAL_CSTRING("content-length"), - header, - res); - MOZ_ASSERT(!res.Failed()); - - LOG_V("HttpServer::Connection::ConsumeLine(%p) - content-length is \"%s\"", - this, header.get()); - - if (!header.IsEmpty()) { - nsresult rv; - mRemainingBodySize = header.ToInteger(&rv); - NS_ENSURE_SUCCESS(rv, rv); - } else { - mRemainingBodySize = 0; - } - - if (mRemainingBodySize) { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Starting consume body", this); - mState = eBody; - - // We use an unlimited buffer size here to ensure - // that we get to the next request even if the webpage hangs on - // to the request indefinitely without consuming the body. - nsCOMPtr<nsIInputStream> input; - nsCOMPtr<nsIOutputStream> output; - nsresult rv = NS_NewPipe(getter_AddRefs(input), - getter_AddRefs(output), - 0, // Segment size - UINT32_MAX, // Unlimited buffer size - false, // not nonBlockingInput - true); // nonBlockingOutput - NS_ENSURE_SUCCESS(rv, rv); - - mCurrentRequestBody = do_QueryInterface(output); - mPendingReq->SetBody(input); - } else { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - No body", this); - mState = eRequestLine; - } - - mPendingRequests.AppendElement(PendingRequest(mPendingReq, nullptr)); - - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Fire OnRequest", this); - - RefPtr<HttpServerListener> listener = mServer->mListener; - RefPtr<InternalRequest> request = mPendingReq.forget(); - nsCOMPtr<nsIRunnable> event = - NS_NewRunnableFunction([listener, request] () - { - listener->OnRequest(request); - }); - NS_DispatchToCurrentThread(event); - - mPendingReqVersion = 0; - - return NS_OK; - } - - // Parse header line - if (aBuffer[0] == ' ' || aBuffer[0] == '\t') { - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Add to header %s", - this, - mPendingHeaderName.get()); - - NS_ENSURE_FALSE(mPendingHeaderName.IsEmpty(), - NS_ERROR_UNEXPECTED); - - // We might need to do whitespace trimming/compression here. - mPendingHeaderValue.Append(aBuffer, aLength); - return NS_OK; - } - - MaybeAddPendingHeader(); - - const char* colon = static_cast<const char*>(memchr(aBuffer, ':', aLength)); - NS_ENSURE_TRUE(colon, NS_ERROR_UNEXPECTED); - - ToLowerCase(Substring(aBuffer, colon - aBuffer), mPendingHeaderName); - mPendingHeaderValue.Assign(colon + 1, aLength - (colon - aBuffer) - 1); - - NS_ENSURE_TRUE(NS_IsValidHTTPToken(mPendingHeaderName), - NS_ERROR_UNEXPECTED); - - LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsed header %s", - this, - mPendingHeaderName.get()); - - return NS_OK; -} - -void -HttpServer::Connection::MaybeAddPendingHeader() -{ - if (mPendingHeaderName.IsEmpty()) { - return; - } - - // We might need to do more whitespace trimming/compression here. - mPendingHeaderValue.Trim(" \t"); - - ErrorResult rv; - mPendingReq->Headers()->Append(mPendingHeaderName, mPendingHeaderValue, rv); - mPendingHeaderName.Truncate(); -} - -bool -HttpServer::Connection::TryHandleResponse(InternalRequest* aRequest, - InternalResponse* aResponse) -{ - bool handledResponse = false; - for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) { - PendingRequest& pending = mPendingRequests[i]; - if (pending.first() == aRequest) { - MOZ_ASSERT(!handledResponse); - MOZ_ASSERT(!pending.second()); - - pending.second() = aResponse; - if (i != 0) { - return true; - } - handledResponse = true; - } - - if (handledResponse && !pending.second()) { - // Shortcut if we've handled the response, and - // we don't have more responses to send - return true; - } - - if (i == 0 && pending.second()) { - RefPtr<InternalResponse> resp = pending.second().forget(); - mPendingRequests.RemoveElementAt(0); - QueueResponse(resp); - --i; - } - } - - return handledResponse; -} - -already_AddRefed<nsITransportProvider> -HttpServer::Connection::HandleAcceptWebSocket(const Optional<nsAString>& aProtocol, - ErrorResult& aRv) -{ - MOZ_ASSERT(mPendingWebSocketRequest); - - RefPtr<InternalResponse> response = - new InternalResponse(101, NS_LITERAL_CSTRING("Switching Protocols")); - - InternalHeaders* headers = response->Headers(); - headers->Set(NS_LITERAL_CSTRING("Upgrade"), - NS_LITERAL_CSTRING("websocket"), - aRv); - headers->Set(NS_LITERAL_CSTRING("Connection"), - NS_LITERAL_CSTRING("Upgrade"), - aRv); - if (aProtocol.WasPassed()) { - NS_ConvertUTF16toUTF8 protocol(aProtocol.Value()); - nsAutoCString reqProtocols; - mPendingWebSocketRequest->Headers()-> - GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv); - if (!ContainsToken(reqProtocols, protocol)) { - // Should throw a better error here - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), - protocol, aRv); - } - - nsAutoCString key, hash; - mPendingWebSocketRequest->Headers()-> - GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Key"), key, aRv); - nsresult rv = mozilla::net::CalculateWebSocketHashedSecret(key, hash); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Accept"), hash, aRv); - - nsAutoCString extensions, negotiatedExtensions; - mPendingWebSocketRequest->Headers()-> - GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions, aRv); - mozilla::net::ProcessServerWebSocketExtensions(extensions, - negotiatedExtensions); - if (!negotiatedExtensions.IsEmpty()) { - headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), - negotiatedExtensions, aRv); - } - - RefPtr<TransportProvider> result = new TransportProvider(); - mWebSocketTransportProvider = result; - - QueueResponse(response); - - return result.forget(); -} - -void -HttpServer::Connection::HandleWebSocketResponse(InternalResponse* aResponse) -{ - MOZ_ASSERT(mPendingWebSocketRequest); - - mState = eRequestLine; - mPendingWebSocketRequest = nullptr; - mInput->AsyncWait(this, 0, 0, NS_GetCurrentThread()); - - QueueResponse(aResponse); -} - -void -HttpServer::Connection::QueueResponse(InternalResponse* aResponse) -{ - bool chunked = false; - - RefPtr<InternalHeaders> headers = new InternalHeaders(*aResponse->Headers()); - { - ErrorResult res; - headers->SetGuard(HeadersGuardEnum::None, res); - } - nsCOMPtr<nsIInputStream> body; - int64_t bodySize; - aResponse->GetBody(getter_AddRefs(body), &bodySize); - - if (body && bodySize >= 0) { - nsCString sizeStr; - sizeStr.AppendInt(bodySize); - - LOG_V("HttpServer::Connection::QueueResponse(%p) - " - "Setting content-length to %s", - this, sizeStr.get()); - - ErrorResult res; - headers->Set(NS_LITERAL_CSTRING("content-length"), sizeStr, res); - } else if (body) { - // Use chunked transfer encoding - LOG_V("HttpServer::Connection::QueueResponse(%p) - Chunked transfer-encoding", - this); - - ErrorResult res; - headers->Set(NS_LITERAL_CSTRING("transfer-encoding"), - NS_LITERAL_CSTRING("chunked"), - res); - headers->Delete(NS_LITERAL_CSTRING("content-length"), res); - chunked = true; - - } else { - LOG_V("HttpServer::Connection::QueueResponse(%p) - " - "No body - setting content-length to 0", this); - - ErrorResult res; - headers->Set(NS_LITERAL_CSTRING("content-length"), - NS_LITERAL_CSTRING("0"), res); - } - - nsCString head(NS_LITERAL_CSTRING("HTTP/1.1 ")); - head.AppendInt(aResponse->GetStatus()); - // XXX is the statustext security checked? - head.Append(NS_LITERAL_CSTRING(" ") + - aResponse->GetStatusText() + - NS_LITERAL_CSTRING("\r\n")); - - AutoTArray<InternalHeaders::Entry, 16> entries; - headers->GetEntries(entries); - - for (auto header : entries) { - head.Append(header.mName + - NS_LITERAL_CSTRING(": ") + - header.mValue + - NS_LITERAL_CSTRING("\r\n")); - } - - head.Append(NS_LITERAL_CSTRING("\r\n")); - - mOutputBuffers.AppendElement()->mString = head; - if (body) { - OutputBuffer* bodyBuffer = mOutputBuffers.AppendElement(); - bodyBuffer->mStream = body; - bodyBuffer->mChunked = chunked; - } - - OnOutputStreamReady(mOutput); -} - -namespace { - -typedef MozPromise<nsresult, bool, false> StreamCopyPromise; - -class StreamCopier final : public nsIOutputStreamCallback - , public nsIInputStreamCallback - , public nsIRunnable -{ -public: - static RefPtr<StreamCopyPromise> - Copy(nsIInputStream* aSource, nsIAsyncOutputStream* aSink, - bool aChunked) - { - RefPtr<StreamCopier> copier = new StreamCopier(aSource, aSink, aChunked); - - RefPtr<StreamCopyPromise> p = copier->mPromise.Ensure(__func__); - - nsresult rv = copier->mTarget->Dispatch(copier, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - copier->mPromise.Resolve(rv, __func__); - } - - return p; - } - - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIINPUTSTREAMCALLBACK - NS_DECL_NSIOUTPUTSTREAMCALLBACK - NS_DECL_NSIRUNNABLE - -private: - StreamCopier(nsIInputStream* aSource, nsIAsyncOutputStream* aSink, - bool aChunked) - : mSource(aSource) - , mAsyncSource(do_QueryInterface(aSource)) - , mSink(aSink) - , mTarget(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID)) - , mChunkRemaining(0) - , mChunked(aChunked) - , mAddedFinalSeparator(false) - , mFirstChunk(aChunked) - { - } - ~StreamCopier() {} - - static nsresult FillOutputBufferHelper(nsIOutputStream* aOutStr, - void* aClosure, - char* aBuffer, - uint32_t aOffset, - uint32_t aCount, - uint32_t* aCountRead); - nsresult FillOutputBuffer(char* aBuffer, - uint32_t aCount, - uint32_t* aCountRead); - - nsCOMPtr<nsIInputStream> mSource; - nsCOMPtr<nsIAsyncInputStream> mAsyncSource; - nsCOMPtr<nsIAsyncOutputStream> mSink; - MozPromiseHolder<StreamCopyPromise> mPromise; - nsCOMPtr<nsIEventTarget> mTarget; // XXX we should cache this somewhere - uint32_t mChunkRemaining; - nsCString mSeparator; - bool mChunked; - bool mAddedFinalSeparator; - bool mFirstChunk; -}; - -NS_IMPL_ISUPPORTS(StreamCopier, - nsIOutputStreamCallback, - nsIInputStreamCallback, - nsIRunnable) - -struct WriteState -{ - StreamCopier* copier; - nsresult sourceRv; -}; - -// This function only exists to enable FillOutputBuffer to be a non-static -// function where we can use member variables more easily. -nsresult -StreamCopier::FillOutputBufferHelper(nsIOutputStream* aOutStr, - void* aClosure, - char* aBuffer, - uint32_t aOffset, - uint32_t aCount, - uint32_t* aCountRead) -{ - WriteState* ws = static_cast<WriteState*>(aClosure); - ws->sourceRv = ws->copier->FillOutputBuffer(aBuffer, aCount, aCountRead); - return ws->sourceRv; -} - -nsresult -CheckForEOF(nsIInputStream* aIn, - void* aClosure, - const char* aBuffer, - uint32_t aToOffset, - uint32_t aCount, - uint32_t* aWriteCount) -{ - *static_cast<bool*>(aClosure) = true; - *aWriteCount = 0; - return NS_BINDING_ABORTED; -} - -nsresult -StreamCopier::FillOutputBuffer(char* aBuffer, - uint32_t aCount, - uint32_t* aCountRead) -{ - nsresult rv = NS_OK; - while (mChunked && mSeparator.IsEmpty() && !mChunkRemaining && - !mAddedFinalSeparator) { - uint64_t avail; - rv = mSource->Available(&avail); - if (rv == NS_BASE_STREAM_CLOSED) { - avail = 0; - rv = NS_OK; - } - NS_ENSURE_SUCCESS(rv, rv); - - mChunkRemaining = avail > UINT32_MAX ? UINT32_MAX : - static_cast<uint32_t>(avail); - - if (!mChunkRemaining) { - // Either it's an non-blocking stream without any data - // currently available, or we're at EOF. Sadly there's no way - // to tell other than to read from the stream. - bool hadData = false; - uint32_t numRead; - rv = mSource->ReadSegments(CheckForEOF, &hadData, 1, &numRead); - if (rv == NS_BASE_STREAM_CLOSED) { - avail = 0; - rv = NS_OK; - } - NS_ENSURE_SUCCESS(rv, rv); - MOZ_ASSERT(numRead == 0); - - if (hadData) { - // The source received data between the call to Available and the - // call to ReadSegments. Restart with a new call to Available - continue; - } - - // We're at EOF, write a separator with 0 - mAddedFinalSeparator = true; - } - - if (mFirstChunk) { - mFirstChunk = false; - MOZ_ASSERT(mSeparator.IsEmpty()); - } else { - // For all chunks except the first, add the newline at the end - // of the previous chunk of data - mSeparator.AssignLiteral("\r\n"); - } - mSeparator.AppendInt(mChunkRemaining, 16); - mSeparator.AppendLiteral("\r\n"); - - if (mAddedFinalSeparator) { - mSeparator.AppendLiteral("\r\n"); - } - - break; - } - - // If we're doing chunked encoding, we should either have a chunk size, - // or we should have reached the end of the input stream. - MOZ_ASSERT_IF(mChunked, mChunkRemaining || mAddedFinalSeparator); - // We should only have a separator if we're doing chunked encoding - MOZ_ASSERT_IF(!mSeparator.IsEmpty(), mChunked); - - if (!mSeparator.IsEmpty()) { - *aCountRead = std::min(mSeparator.Length(), aCount); - memcpy(aBuffer, mSeparator.BeginReading(), *aCountRead); - mSeparator.Cut(0, *aCountRead); - rv = NS_OK; - } else if (mChunked) { - *aCountRead = 0; - if (mChunkRemaining) { - rv = mSource->Read(aBuffer, - std::min(aCount, mChunkRemaining), - aCountRead); - mChunkRemaining -= *aCountRead; - } - } else { - rv = mSource->Read(aBuffer, aCount, aCountRead); - } - - if (NS_SUCCEEDED(rv) && *aCountRead == 0) { - rv = NS_BASE_STREAM_CLOSED; - } - - return rv; -} - -NS_IMETHODIMP -StreamCopier::Run() -{ - nsresult rv; - while (1) { - WriteState state = { this, NS_OK }; - uint32_t written; - rv = mSink->WriteSegments(FillOutputBufferHelper, &state, - mozilla::net::nsIOService::gDefaultSegmentSize, - &written); - MOZ_ASSERT(NS_SUCCEEDED(rv) || NS_SUCCEEDED(state.sourceRv)); - if (rv == NS_BASE_STREAM_WOULD_BLOCK) { - mSink->AsyncWait(this, 0, 0, mTarget); - return NS_OK; - } - if (NS_FAILED(rv)) { - mPromise.Resolve(rv, __func__); - return NS_OK; - } - - if (state.sourceRv == NS_BASE_STREAM_WOULD_BLOCK) { - MOZ_ASSERT(mAsyncSource); - mAsyncSource->AsyncWait(this, 0, 0, mTarget); - mSink->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, - 0, mTarget); - - return NS_OK; - } - if (state.sourceRv == NS_BASE_STREAM_CLOSED) { - // We're done! - // No longer interested in callbacks about either stream closing - mSink->AsyncWait(nullptr, 0, 0, nullptr); - if (mAsyncSource) { - mAsyncSource->AsyncWait(nullptr, 0, 0, nullptr); - } - - mSource->Close(); - mSource = nullptr; - mAsyncSource = nullptr; - mSink = nullptr; - - mPromise.Resolve(NS_OK, __func__); - - return NS_OK; - } - - if (NS_FAILED(state.sourceRv)) { - mPromise.Resolve(state.sourceRv, __func__); - return NS_OK; - } - } - - MOZ_ASSUME_UNREACHABLE_MARKER(); -} - -NS_IMETHODIMP -StreamCopier::OnInputStreamReady(nsIAsyncInputStream* aStream) -{ - MOZ_ASSERT(aStream == mAsyncSource || - (!mSource && !mAsyncSource && !mSink)); - return mSource ? Run() : NS_OK; -} - -NS_IMETHODIMP -StreamCopier::OnOutputStreamReady(nsIAsyncOutputStream* aStream) -{ - MOZ_ASSERT(aStream == mSink || - (!mSource && !mAsyncSource && !mSink)); - return mSource ? Run() : NS_OK; -} - -} // namespace - -NS_IMETHODIMP -HttpServer::Connection::OnOutputStreamReady(nsIAsyncOutputStream* aStream) -{ - MOZ_ASSERT(aStream == mOutput || !mOutput); - if (!mOutput) { - return NS_OK; - } - - nsresult rv; - - while (!mOutputBuffers.IsEmpty()) { - if (!mOutputBuffers[0].mStream) { - nsCString& buffer = mOutputBuffers[0].mString; - while (!buffer.IsEmpty()) { - uint32_t written = 0; - rv = mOutput->Write(buffer.BeginReading(), - buffer.Length(), - &written); - - buffer.Cut(0, written); - - if (rv == NS_BASE_STREAM_WOULD_BLOCK) { - return mOutput->AsyncWait(this, 0, 0, NS_GetCurrentThread()); - } - - if (NS_FAILED(rv)) { - Close(); - return NS_OK; - } - } - mOutputBuffers.RemoveElementAt(0); - } else { - if (mOutputCopy) { - // we're already copying the stream - return NS_OK; - } - - mOutputCopy = - StreamCopier::Copy(mOutputBuffers[0].mStream, - mOutput, - mOutputBuffers[0].mChunked); - - RefPtr<Connection> self = this; - - mOutputCopy-> - Then(AbstractThread::MainThread(), - __func__, - [self, this] (nsresult aStatus) { - MOZ_ASSERT(mOutputBuffers[0].mStream); - LOG_V("HttpServer::Connection::OnOutputStreamReady(%p) - " - "Sent body. Status 0x%lx", - this, aStatus); - - mOutputBuffers.RemoveElementAt(0); - mOutputCopy = nullptr; - OnOutputStreamReady(mOutput); - }, - [] (bool) { MOZ_ASSERT_UNREACHABLE("Reject unexpected"); }); - } - } - - if (mPendingRequests.IsEmpty()) { - if (mCloseAfterRequest) { - LOG_V("HttpServer::Connection::OnOutputStreamReady(%p) - Closing channel", - this); - Close(); - } else if (mWebSocketTransportProvider) { - mInput->AsyncWait(nullptr, 0, 0, nullptr); - mOutput->AsyncWait(nullptr, 0, 0, nullptr); - - mWebSocketTransportProvider->SetTransport(mTransport, mInput, mOutput); - mTransport = nullptr; - mInput = nullptr; - mOutput = nullptr; - mWebSocketTransportProvider = nullptr; - } - } - - return NS_OK; -} - -void -HttpServer::Connection::Close() -{ - if (!mTransport) { - MOZ_ASSERT(!mOutput && !mInput); - return; - } - - mTransport->Close(NS_BINDING_ABORTED); - if (mInput) { - mInput->Close(); - mInput = nullptr; - } - if (mOutput) { - mOutput->Close(); - mOutput = nullptr; - } - - mTransport = nullptr; - - mInputBuffer.Truncate(); - mOutputBuffers.Clear(); - mPendingRequests.Clear(); -} - - -} // namespace net -} // namespace mozilla diff --git a/dom/flyweb/HttpServer.h b/dom/flyweb/HttpServer.h deleted file mode 100644 index dab601c24..000000000 --- a/dom/flyweb/HttpServer.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- 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_HttpServer_h -#define mozilla_dom_HttpServer_h - -#include "nsISupportsImpl.h" -#include "mozilla/DOMEventTargetHelper.h" -#include "nsITLSServerSocket.h" -#include "nsIAsyncInputStream.h" -#include "nsIAsyncOutputStream.h" -#include "mozilla/Variant.h" -#include "nsIRequestObserver.h" -#include "mozilla/MozPromise.h" -#include "nsITransportProvider.h" -#include "nsILocalCertService.h" - -class nsIX509Cert; - -namespace mozilla { -namespace dom { - -extern bool -ContainsToken(const nsCString& aList, const nsCString& aToken); - -class InternalRequest; -class InternalResponse; - -class HttpServerListener -{ -public: - // switch to NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING when that lands - NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0; - NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0; - - virtual void OnServerStarted(nsresult aStatus) = 0; - virtual void OnRequest(InternalRequest* aRequest) = 0; - virtual void OnWebSocket(InternalRequest* aConnectRequest) = 0; - virtual void OnServerClose() = 0; -}; - -class HttpServer final : public nsIServerSocketListener, - public nsILocalCertGetCallback -{ -public: - HttpServer(); - - NS_DECL_ISUPPORTS - NS_DECL_NSISERVERSOCKETLISTENER - NS_DECL_NSILOCALCERTGETCALLBACK - - void Init(int32_t aPort, bool aHttps, HttpServerListener* aListener); - - void SendResponse(InternalRequest* aRequest, InternalResponse* aResponse); - already_AddRefed<nsITransportProvider> - AcceptWebSocket(InternalRequest* aConnectRequest, - const Optional<nsAString>& aProtocol, - ErrorResult& aRv); - void SendWebSocketResponse(InternalRequest* aConnectRequest, - InternalResponse* aResponse); - - void Close(); - - void GetCertKey(nsACString& aKey); - - int32_t GetPort() - { - return mPort; - } - -private: - ~HttpServer(); - - nsresult StartServerSocket(nsIX509Cert* aCert); - void NotifyStarted(nsresult aStatus); - - class TransportProvider final : public nsITransportProvider - { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSITRANSPORTPROVIDER - - void SetTransport(nsISocketTransport* aTransport, - nsIAsyncInputStream* aInput, - nsIAsyncOutputStream* aOutput); - - private: - virtual ~TransportProvider(); - void MaybeNotify(); - - nsCOMPtr<nsIHttpUpgradeListener> mListener; - nsCOMPtr<nsISocketTransport> mTransport; - nsCOMPtr<nsIAsyncInputStream> mInput; - nsCOMPtr<nsIAsyncOutputStream> mOutput; - }; - - class Connection final : public nsIInputStreamCallback - , public nsIOutputStreamCallback - , public nsITLSServerSecurityObserver - { - public: - Connection(nsISocketTransport* aTransport, - HttpServer* aServer, - nsresult& rv); - - NS_DECL_ISUPPORTS - NS_DECL_NSIINPUTSTREAMCALLBACK - NS_DECL_NSIOUTPUTSTREAMCALLBACK - NS_DECL_NSITLSSERVERSECURITYOBSERVER - - bool TryHandleResponse(InternalRequest* aRequest, - InternalResponse* aResponse); - already_AddRefed<nsITransportProvider> - HandleAcceptWebSocket(const Optional<nsAString>& aProtocol, - ErrorResult& aRv); - void HandleWebSocketResponse(InternalResponse* aResponse); - bool HasPendingWebSocketRequest(InternalRequest* aRequest) - { - return aRequest == mPendingWebSocketRequest; - } - - void Close(); - - private: - ~Connection(); - - void SetSecurityObserver(bool aListen); - - static nsresult ReadSegmentsFunc(nsIInputStream* aIn, - void* aClosure, - const char* aBuffer, - uint32_t aToOffset, - uint32_t aCount, - uint32_t* aWriteCount); - nsresult ConsumeInput(const char*& aBuffer, - const char* aEnd); - nsresult ConsumeLine(const char* aBuffer, - size_t aLength); - void MaybeAddPendingHeader(); - - void QueueResponse(InternalResponse* aResponse); - - RefPtr<HttpServer> mServer; - nsCOMPtr<nsISocketTransport> mTransport; - nsCOMPtr<nsIAsyncInputStream> mInput; - nsCOMPtr<nsIAsyncOutputStream> mOutput; - - enum { eRequestLine, eHeaders, eBody, ePause } mState; - RefPtr<InternalRequest> mPendingReq; - uint32_t mPendingReqVersion; - nsCString mInputBuffer; - nsCString mPendingHeaderName; - nsCString mPendingHeaderValue; - uint32_t mRemainingBodySize; - nsCOMPtr<nsIAsyncOutputStream> mCurrentRequestBody; - bool mCloseAfterRequest; - - typedef Pair<RefPtr<InternalRequest>, - RefPtr<InternalResponse>> PendingRequest; - nsTArray<PendingRequest> mPendingRequests; - RefPtr<MozPromise<nsresult, bool, false>> mOutputCopy; - - RefPtr<InternalRequest> mPendingWebSocketRequest; - RefPtr<TransportProvider> mWebSocketTransportProvider; - - struct OutputBuffer { - nsCString mString; - nsCOMPtr<nsIInputStream> mStream; - bool mChunked; - }; - - nsTArray<OutputBuffer> mOutputBuffers; - }; - - friend class Connection; - - RefPtr<HttpServerListener> mListener; - nsCOMPtr<nsIServerSocket> mServerSocket; - nsCOMPtr<nsIX509Cert> mCert; - - nsTArray<RefPtr<Connection>> mConnections; - - int32_t mPort; - bool mHttps; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_HttpServer_h diff --git a/dom/flyweb/PFlyWebPublishedServer.ipdl b/dom/flyweb/PFlyWebPublishedServer.ipdl deleted file mode 100644 index 4d08a47fc..000000000 --- a/dom/flyweb/PFlyWebPublishedServer.ipdl +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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 protocol PContent; -include protocol PSendStream; -include protocol PFileDescriptorSet; -include protocol PTransportProvider; -include FetchTypes; -include ChannelInfo; -include PBackgroundSharedTypes; - -namespace mozilla { -namespace dom { - -async protocol PFlyWebPublishedServer -{ - manager PContent; - -child: - async ServerReady(nsresult aStatus); - async FetchRequest(IPCInternalRequest aRequest, uint64_t aRequestId); - async WebSocketRequest(IPCInternalRequest aRequest, uint64_t aRequestId, - PTransportProvider aProvider); - async ServerClose(); - -parent: - async __delete__(); - - async FetchResponse(IPCInternalResponse aResponse, uint64_t aRequestId); - async WebSocketResponse(IPCInternalResponse aResponse, uint64_t aRequestId); - async WebSocketAccept(nsString aProtocol, uint64_t aRequestId); -}; - -} // namespace dom -} // namespace mozilla diff --git a/dom/flyweb/moz.build b/dom/flyweb/moz.build deleted file mode 100644 index aa8c034b7..000000000 --- a/dom/flyweb/moz.build +++ /dev/null @@ -1,42 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS.mozilla.dom += [ - 'FlyWebDiscoveryManager.h', - 'FlyWebPublishedServer.h', - 'FlyWebPublishedServerIPC.h', - 'FlyWebPublishOptionsIPCSerializer.h', - 'FlyWebServerEvents.h', - 'FlyWebService.h', - 'HttpServer.h', -] - -UNIFIED_SOURCES += [ - 'FlyWebDiscoveryManager.cpp', - 'FlyWebPublishedServer.cpp', - 'FlyWebServerEvents.cpp', - 'FlyWebService.cpp', - 'HttpServer.cpp' -] - -IPDL_SOURCES += [ - 'PFlyWebPublishedServer.ipdl', -] - -FINAL_LIBRARY = 'xul' - -LOCAL_INCLUDES += [ - '/dom/base', - '/netwerk/base', - '/netwerk/dns', - '/netwerk/protocol/websocket', - '/xpcom/io' -] - -include('/ipc/chromium/chromium-config.mozbuild') - -if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wshadow'] diff --git a/dom/heapsnapshot/.gitattributes b/dom/heapsnapshot/.gitattributes new file mode 100644 index 000000000..44e248a8d --- /dev/null +++ b/dom/heapsnapshot/.gitattributes @@ -0,0 +1 @@ +CoreDump.pb.* binary diff --git a/dom/heapsnapshot/AutoMemMap.cpp b/dom/heapsnapshot/AutoMemMap.cpp new file mode 100644 index 000000000..e725a99c6 --- /dev/null +++ b/dom/heapsnapshot/AutoMemMap.cpp @@ -0,0 +1,64 @@ +/* -*- 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 "mozilla/devtools/AutoMemMap.h" + +#include "mozilla/Unused.h" +#include "nsDebug.h" + +namespace mozilla { +namespace devtools { + +AutoMemMap::~AutoMemMap() +{ + if (addr) { + Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS); + addr = nullptr; + } + + if (fileMap) { + Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS); + fileMap = nullptr; + } + + if (fd) { + Unused << NS_WARN_IF(PR_Close(fd) != PR_SUCCESS); + fd = nullptr; + } +} + +nsresult +AutoMemMap::init(const char* filePath, int flags, int mode, PRFileMapProtect prot) +{ + MOZ_ASSERT(!fd); + MOZ_ASSERT(!fileMap); + MOZ_ASSERT(!addr); + + if (PR_GetFileInfo64(filePath, &fileInfo) != PR_SUCCESS) + return NS_ERROR_FILE_NOT_FOUND; + + // Check if the file is too big to memmap. + if (fileInfo.size > int64_t(UINT32_MAX)) + return NS_ERROR_INVALID_ARG; + auto length = uint32_t(fileInfo.size); + + fd = PR_Open(filePath, flags, flags); + if (!fd) + return NS_ERROR_UNEXPECTED; + + fileMap = PR_CreateFileMap(fd, fileInfo.size, prot); + if (!fileMap) + return NS_ERROR_UNEXPECTED; + + addr = PR_MemMap(fileMap, 0, length); + if (!addr) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/AutoMemMap.h b/dom/heapsnapshot/AutoMemMap.h new file mode 100644 index 000000000..537d68004 --- /dev/null +++ b/dom/heapsnapshot/AutoMemMap.h @@ -0,0 +1,75 @@ +/* -*- 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_devtools_AutoMemMap_h +#define mozilla_devtools_AutoMemMap_h + +#include <prio.h> +#include "mozilla/GuardObjects.h" + +namespace mozilla { +namespace devtools { + +// # AutoMemMap +// +// AutoMemMap is an RAII class to manage mapping a file to memory. It is a +// wrapper aorund managing opening and closing a file and calling PR_MemMap and +// PR_MemUnmap. +// +// Example usage: +// +// { +// AutoMemMap mm; +// if (NS_FAILED(mm.init("/path/to/desired/file"))) { +// // Handle the error however you see fit. +// return false; +// } +// +// doStuffWithMappedMemory(mm.address()); +// } +// // The memory is automatically unmapped when the AutoMemMap leaves scope. +class MOZ_RAII AutoMemMap +{ + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; + + PRFileInfo64 fileInfo; + PRFileDesc* fd; + PRFileMap* fileMap; + void* addr; + + AutoMemMap(const AutoMemMap& aOther) = delete; + void operator=(const AutoMemMap& aOther) = delete; + +public: + explicit AutoMemMap(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) + : fd(nullptr) + , fileMap(nullptr) + , addr(nullptr) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + }; + ~AutoMemMap(); + + // Initialize this AutoMemMap. + nsresult init(const char* filePath, int flags = PR_RDONLY, int mode = 0, + PRFileMapProtect prot = PR_PROT_READONLY); + + // Get the size of the memory mapped file. + uint32_t size() const { + MOZ_ASSERT(fileInfo.size <= UINT32_MAX, + "Should only call size() if init() succeeded."); + return uint32_t(fileInfo.size); + } + + // Get the mapped memory. + void* address() { MOZ_ASSERT(addr); return addr; } + const void* address() const { MOZ_ASSERT(addr); return addr; } +}; + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_AutoMemMap_h diff --git a/dom/heapsnapshot/CoreDump.pb.cc b/dom/heapsnapshot/CoreDump.pb.cc new file mode 100644 index 000000000..6c7c0e8a4 --- /dev/null +++ b/dom/heapsnapshot/CoreDump.pb.cc @@ -0,0 +1,2542 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: CoreDump.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "CoreDump.pb.h" + +#include <algorithm> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/once.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/wire_format_lite_inl.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/generated_message_reflection.h> +#include <google/protobuf/reflection_ops.h> +#include <google/protobuf/wire_format.h> +// @@protoc_insertion_point(includes) + +namespace mozilla { +namespace devtools { +namespace protobuf { + +namespace { + +const ::google::protobuf::Descriptor* Metadata_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Metadata_reflection_ = NULL; +const ::google::protobuf::Descriptor* StackFrame_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + StackFrame_reflection_ = NULL; +struct StackFrameOneofInstance { + const ::mozilla::devtools::protobuf::StackFrame_Data* data_; + ::google::protobuf::uint64 ref_; +}* StackFrame_default_oneof_instance_ = NULL; +const ::google::protobuf::Descriptor* StackFrame_Data_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + StackFrame_Data_reflection_ = NULL; +struct StackFrame_DataOneofInstance { + const ::std::string* source_; + ::google::protobuf::uint64 sourceref_; + const ::std::string* functiondisplayname_; + ::google::protobuf::uint64 functiondisplaynameref_; +}* StackFrame_Data_default_oneof_instance_ = NULL; +const ::google::protobuf::Descriptor* Node_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Node_reflection_ = NULL; +struct NodeOneofInstance { + const ::std::string* typename__; + ::google::protobuf::uint64 typenameref_; + const ::std::string* jsobjectclassname_; + ::google::protobuf::uint64 jsobjectclassnameref_; + const ::std::string* scriptfilename_; + ::google::protobuf::uint64 scriptfilenameref_; +}* Node_default_oneof_instance_ = NULL; +const ::google::protobuf::Descriptor* Edge_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Edge_reflection_ = NULL; +struct EdgeOneofInstance { + const ::std::string* name_; + ::google::protobuf::uint64 nameref_; +}* Edge_default_oneof_instance_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_CoreDump_2eproto() { + protobuf_AddDesc_CoreDump_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "CoreDump.proto"); + GOOGLE_CHECK(file != NULL); + Metadata_descriptor_ = file->message_type(0); + static const int Metadata_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Metadata, timestamp_), + }; + Metadata_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Metadata_descriptor_, + Metadata::default_instance_, + Metadata_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Metadata, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Metadata, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Metadata)); + StackFrame_descriptor_ = file->message_type(1); + static const int StackFrame_offsets_[3] = { + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_default_oneof_instance_, data_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_default_oneof_instance_, ref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame, StackFrameType_), + }; + StackFrame_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + StackFrame_descriptor_, + StackFrame::default_instance_, + StackFrame_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame, _unknown_fields_), + -1, + StackFrame_default_oneof_instance_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame, _oneof_case_[0]), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(StackFrame)); + StackFrame_Data_descriptor_ = StackFrame_descriptor_->nested_type(0); + static const int StackFrame_Data_offsets_[12] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, id_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, parent_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, line_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, column_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_Data_default_oneof_instance_, source_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_Data_default_oneof_instance_, sourceref_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_Data_default_oneof_instance_, functiondisplayname_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(StackFrame_Data_default_oneof_instance_, functiondisplaynameref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, issystem_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, isselfhosted_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, SourceOrRef_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, FunctionDisplayNameOrRef_), + }; + StackFrame_Data_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + StackFrame_Data_descriptor_, + StackFrame_Data::default_instance_, + StackFrame_Data_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, _unknown_fields_), + -1, + StackFrame_Data_default_oneof_instance_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(StackFrame_Data, _oneof_case_[0]), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(StackFrame_Data)); + Node_descriptor_ = file->message_type(2); + static const int Node_offsets_[14] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, id_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, typename__), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, typenameref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, size_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, edges_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, allocationstack_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, jsobjectclassname_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, jsobjectclassnameref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, coarsetype_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, scriptfilename_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Node_default_oneof_instance_, scriptfilenameref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, TypeNameOrRef_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, JSObjectClassNameOrRef_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, ScriptFilenameOrRef_), + }; + Node_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Node_descriptor_, + Node::default_instance_, + Node_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, _unknown_fields_), + -1, + Node_default_oneof_instance_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, _oneof_case_[0]), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Node)); + Edge_descriptor_ = file->message_type(3); + static const int Edge_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Edge, referent_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Edge_default_oneof_instance_, name_), + PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(Edge_default_oneof_instance_, nameref_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Edge, EdgeNameOrRef_), + }; + Edge_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Edge_descriptor_, + Edge::default_instance_, + Edge_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Edge, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Edge, _unknown_fields_), + -1, + Edge_default_oneof_instance_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Edge, _oneof_case_[0]), + ::google::protobuf::DescriptorPool::generated_pool(), + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(Edge)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +inline void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_CoreDump_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Metadata_descriptor_, &Metadata::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + StackFrame_descriptor_, &StackFrame::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + StackFrame_Data_descriptor_, &StackFrame_Data::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Node_descriptor_, &Node::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Edge_descriptor_, &Edge::default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_CoreDump_2eproto() { + delete Metadata::default_instance_; + delete Metadata_reflection_; + delete StackFrame::default_instance_; + delete StackFrame_default_oneof_instance_; + delete StackFrame_reflection_; + delete StackFrame_Data::default_instance_; + delete StackFrame_Data_default_oneof_instance_; + delete StackFrame_Data_reflection_; + delete Node::default_instance_; + delete Node_default_oneof_instance_; + delete Node_reflection_; + delete Edge::default_instance_; + delete Edge_default_oneof_instance_; + delete Edge_reflection_; +} + +void protobuf_AddDesc_CoreDump_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\016CoreDump.proto\022\031mozilla.devtools.proto" + "buf\"\035\n\010Metadata\022\021\n\ttimeStamp\030\001 \001(\004\"\216\003\n\nS" + "tackFrame\022:\n\004data\030\001 \001(\0132*.mozilla.devtoo" + "ls.protobuf.StackFrame.DataH\000\022\r\n\003ref\030\002 \001" + "(\004H\000\032\242\002\n\004Data\022\n\n\002id\030\001 \001(\004\0225\n\006parent\030\002 \001(" + "\0132%.mozilla.devtools.protobuf.StackFrame" + "\022\014\n\004line\030\003 \001(\r\022\016\n\006column\030\004 \001(\r\022\020\n\006source" + "\030\005 \001(\014H\000\022\023\n\tsourceRef\030\006 \001(\004H\000\022\035\n\023functio" + "nDisplayName\030\007 \001(\014H\001\022 \n\026functionDisplayN" + "ameRef\030\010 \001(\004H\001\022\020\n\010isSystem\030\t \001(\010\022\024\n\014isSe" + "lfHosted\030\n \001(\010B\r\n\013SourceOrRefB\032\n\030Functio" + "nDisplayNameOrRefB\020\n\016StackFrameType\"\210\003\n\004" + "Node\022\n\n\002id\030\001 \001(\004\022\022\n\010typeName\030\002 \001(\014H\000\022\025\n\013" + "typeNameRef\030\003 \001(\004H\000\022\014\n\004size\030\004 \001(\004\022.\n\005edg" + "es\030\005 \003(\0132\037.mozilla.devtools.protobuf.Edg" + "e\022>\n\017allocationStack\030\006 \001(\0132%.mozilla.dev" + "tools.protobuf.StackFrame\022\033\n\021jsObjectCla" + "ssName\030\007 \001(\014H\001\022\036\n\024jsObjectClassNameRef\030\010" + " \001(\004H\001\022\025\n\ncoarseType\030\t \001(\r:\0010\022\030\n\016scriptF" + "ilename\030\n \001(\014H\002\022\033\n\021scriptFilenameRef\030\013 \001" + "(\004H\002B\017\n\rTypeNameOrRefB\030\n\026JSObjectClassNa" + "meOrRefB\025\n\023ScriptFilenameOrRef\"L\n\004Edge\022\020" + "\n\010referent\030\001 \001(\004\022\016\n\004name\030\002 \001(\014H\000\022\021\n\007name" + "Ref\030\003 \001(\004H\000B\017\n\rEdgeNameOrRef", 948); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "CoreDump.proto", &protobuf_RegisterTypes); + Metadata::default_instance_ = new Metadata(); + StackFrame::default_instance_ = new StackFrame(); + StackFrame_default_oneof_instance_ = new StackFrameOneofInstance; + StackFrame_Data::default_instance_ = new StackFrame_Data(); + StackFrame_Data_default_oneof_instance_ = new StackFrame_DataOneofInstance; + Node::default_instance_ = new Node(); + Node_default_oneof_instance_ = new NodeOneofInstance; + Edge::default_instance_ = new Edge(); + Edge_default_oneof_instance_ = new EdgeOneofInstance; + Metadata::default_instance_->InitAsDefaultInstance(); + StackFrame::default_instance_->InitAsDefaultInstance(); + StackFrame_Data::default_instance_->InitAsDefaultInstance(); + Node::default_instance_->InitAsDefaultInstance(); + Edge::default_instance_->InitAsDefaultInstance(); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_CoreDump_2eproto); +} + +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_CoreDump_2eproto { + StaticDescriptorInitializer_CoreDump_2eproto() { + protobuf_AddDesc_CoreDump_2eproto(); + } +} static_descriptor_initializer_CoreDump_2eproto_; + +// =================================================================== + +#ifndef _MSC_VER +const int Metadata::kTimeStampFieldNumber; +#endif // !_MSC_VER + +Metadata::Metadata() + : ::google::protobuf::Message() { + SharedCtor(); + // @@protoc_insertion_point(constructor:mozilla.devtools.protobuf.Metadata) +} + +void Metadata::InitAsDefaultInstance() { +} + +Metadata::Metadata(const Metadata& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.Metadata) +} + +void Metadata::SharedCtor() { + _cached_size_ = 0; + timestamp_ = GOOGLE_ULONGLONG(0); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +Metadata::~Metadata() { + // @@protoc_insertion_point(destructor:mozilla.devtools.protobuf.Metadata) + SharedDtor(); +} + +void Metadata::SharedDtor() { + if (this != default_instance_) { + } +} + +void Metadata::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Metadata::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Metadata_descriptor_; +} + +const Metadata& Metadata::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_CoreDump_2eproto(); + return *default_instance_; +} + +Metadata* Metadata::default_instance_ = NULL; + +Metadata* Metadata::New() const { + return new Metadata; +} + +void Metadata::Clear() { + timestamp_ = GOOGLE_ULONGLONG(0); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Metadata::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:mozilla.devtools.protobuf.Metadata) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint64 timeStamp = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, ×tamp_))); + set_has_timestamp(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:mozilla.devtools.protobuf.Metadata) + return true; +failure: + // @@protoc_insertion_point(parse_failure:mozilla.devtools.protobuf.Metadata) + return false; +#undef DO_ +} + +void Metadata::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:mozilla.devtools.protobuf.Metadata) + // optional uint64 timeStamp = 1; + if (has_timestamp()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->timestamp(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.Metadata) +} + +::google::protobuf::uint8* Metadata::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:mozilla.devtools.protobuf.Metadata) + // optional uint64 timeStamp = 1; + if (has_timestamp()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->timestamp(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:mozilla.devtools.protobuf.Metadata) + return target; +} + +int Metadata::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint64 timeStamp = 1; + if (has_timestamp()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->timestamp()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Metadata::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Metadata* source = + ::google::protobuf::internal::dynamic_cast_if_available<const Metadata*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Metadata::MergeFrom(const Metadata& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_timestamp()) { + set_timestamp(from.timestamp()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Metadata::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Metadata::CopyFrom(const Metadata& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Metadata::IsInitialized() const { + + return true; +} + +void Metadata::Swap(Metadata* other) { + if (other != this) { + std::swap(timestamp_, other->timestamp_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Metadata::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Metadata_descriptor_; + metadata.reflection = Metadata_reflection_; + return metadata; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int StackFrame_Data::kIdFieldNumber; +const int StackFrame_Data::kParentFieldNumber; +const int StackFrame_Data::kLineFieldNumber; +const int StackFrame_Data::kColumnFieldNumber; +const int StackFrame_Data::kSourceFieldNumber; +const int StackFrame_Data::kSourceRefFieldNumber; +const int StackFrame_Data::kFunctionDisplayNameFieldNumber; +const int StackFrame_Data::kFunctionDisplayNameRefFieldNumber; +const int StackFrame_Data::kIsSystemFieldNumber; +const int StackFrame_Data::kIsSelfHostedFieldNumber; +#endif // !_MSC_VER + +StackFrame_Data::StackFrame_Data() + : ::google::protobuf::Message() { + SharedCtor(); + // @@protoc_insertion_point(constructor:mozilla.devtools.protobuf.StackFrame.Data) +} + +void StackFrame_Data::InitAsDefaultInstance() { + parent_ = const_cast< ::mozilla::devtools::protobuf::StackFrame*>(&::mozilla::devtools::protobuf::StackFrame::default_instance()); + StackFrame_Data_default_oneof_instance_->source_ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + StackFrame_Data_default_oneof_instance_->sourceref_ = GOOGLE_ULONGLONG(0); + StackFrame_Data_default_oneof_instance_->functiondisplayname_ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + StackFrame_Data_default_oneof_instance_->functiondisplaynameref_ = GOOGLE_ULONGLONG(0); +} + +StackFrame_Data::StackFrame_Data(const StackFrame_Data& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.StackFrame.Data) +} + +void StackFrame_Data::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + id_ = GOOGLE_ULONGLONG(0); + parent_ = NULL; + line_ = 0u; + column_ = 0u; + issystem_ = false; + isselfhosted_ = false; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + clear_has_SourceOrRef(); + clear_has_FunctionDisplayNameOrRef(); +} + +StackFrame_Data::~StackFrame_Data() { + // @@protoc_insertion_point(destructor:mozilla.devtools.protobuf.StackFrame.Data) + SharedDtor(); +} + +void StackFrame_Data::SharedDtor() { + if (has_SourceOrRef()) { + clear_SourceOrRef(); + } + if (has_FunctionDisplayNameOrRef()) { + clear_FunctionDisplayNameOrRef(); + } + if (this != default_instance_) { + delete parent_; + } +} + +void StackFrame_Data::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* StackFrame_Data::descriptor() { + protobuf_AssignDescriptorsOnce(); + return StackFrame_Data_descriptor_; +} + +const StackFrame_Data& StackFrame_Data::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_CoreDump_2eproto(); + return *default_instance_; +} + +StackFrame_Data* StackFrame_Data::default_instance_ = NULL; + +StackFrame_Data* StackFrame_Data::New() const { + return new StackFrame_Data; +} + +void StackFrame_Data::clear_SourceOrRef() { + switch(SourceOrRef_case()) { + case kSource: { + delete SourceOrRef_.source_; + break; + } + case kSourceRef: { + // No need to clear + break; + } + case SOURCEORREF_NOT_SET: { + break; + } + } + _oneof_case_[0] = SOURCEORREF_NOT_SET; +} + +void StackFrame_Data::clear_FunctionDisplayNameOrRef() { + switch(FunctionDisplayNameOrRef_case()) { + case kFunctionDisplayName: { + delete FunctionDisplayNameOrRef_.functiondisplayname_; + break; + } + case kFunctionDisplayNameRef: { + // No need to clear + break; + } + case FUNCTIONDISPLAYNAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[1] = FUNCTIONDISPLAYNAMEORREF_NOT_SET; +} + + +void StackFrame_Data::Clear() { +#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \ + &reinterpret_cast<StackFrame_Data*>(16)->f) - \ + reinterpret_cast<char*>(16)) + +#define ZR_(first, last) do { \ + size_t f = OFFSET_OF_FIELD_(first); \ + size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \ + ::memset(&first, 0, n); \ + } while (0) + + if (_has_bits_[0 / 32] & 15) { + ZR_(line_, column_); + id_ = GOOGLE_ULONGLONG(0); + if (has_parent()) { + if (parent_ != NULL) parent_->::mozilla::devtools::protobuf::StackFrame::Clear(); + } + } + ZR_(issystem_, isselfhosted_); + +#undef OFFSET_OF_FIELD_ +#undef ZR_ + + clear_SourceOrRef(); + clear_FunctionDisplayNameOrRef(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool StackFrame_Data::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:mozilla.devtools.protobuf.StackFrame.Data) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint64 id = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &id_))); + set_has_id(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_parent; + break; + } + + // optional .mozilla.devtools.protobuf.StackFrame parent = 2; + case 2: { + if (tag == 18) { + parse_parent: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_parent())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(24)) goto parse_line; + break; + } + + // optional uint32 line = 3; + case 3: { + if (tag == 24) { + parse_line: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &line_))); + set_has_line(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(32)) goto parse_column; + break; + } + + // optional uint32 column = 4; + case 4: { + if (tag == 32) { + parse_column: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &column_))); + set_has_column(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(42)) goto parse_source; + break; + } + + // optional bytes source = 5; + case 5: { + if (tag == 42) { + parse_source: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_source())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(48)) goto parse_sourceRef; + break; + } + + // optional uint64 sourceRef = 6; + case 6: { + if (tag == 48) { + parse_sourceRef: + clear_SourceOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &SourceOrRef_.sourceref_))); + set_has_sourceref(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(58)) goto parse_functionDisplayName; + break; + } + + // optional bytes functionDisplayName = 7; + case 7: { + if (tag == 58) { + parse_functionDisplayName: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_functiondisplayname())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(64)) goto parse_functionDisplayNameRef; + break; + } + + // optional uint64 functionDisplayNameRef = 8; + case 8: { + if (tag == 64) { + parse_functionDisplayNameRef: + clear_FunctionDisplayNameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &FunctionDisplayNameOrRef_.functiondisplaynameref_))); + set_has_functiondisplaynameref(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(72)) goto parse_isSystem; + break; + } + + // optional bool isSystem = 9; + case 9: { + if (tag == 72) { + parse_isSystem: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &issystem_))); + set_has_issystem(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(80)) goto parse_isSelfHosted; + break; + } + + // optional bool isSelfHosted = 10; + case 10: { + if (tag == 80) { + parse_isSelfHosted: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( + input, &isselfhosted_))); + set_has_isselfhosted(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:mozilla.devtools.protobuf.StackFrame.Data) + return true; +failure: + // @@protoc_insertion_point(parse_failure:mozilla.devtools.protobuf.StackFrame.Data) + return false; +#undef DO_ +} + +void StackFrame_Data::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:mozilla.devtools.protobuf.StackFrame.Data) + // optional uint64 id = 1; + if (has_id()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->id(), output); + } + + // optional .mozilla.devtools.protobuf.StackFrame parent = 2; + if (has_parent()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->parent(), output); + } + + // optional uint32 line = 3; + if (has_line()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(3, this->line(), output); + } + + // optional uint32 column = 4; + if (has_column()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(4, this->column(), output); + } + + // optional bytes source = 5; + if (has_source()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 5, this->source(), output); + } + + // optional uint64 sourceRef = 6; + if (has_sourceref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(6, this->sourceref(), output); + } + + // optional bytes functionDisplayName = 7; + if (has_functiondisplayname()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 7, this->functiondisplayname(), output); + } + + // optional uint64 functionDisplayNameRef = 8; + if (has_functiondisplaynameref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(8, this->functiondisplaynameref(), output); + } + + // optional bool isSystem = 9; + if (has_issystem()) { + ::google::protobuf::internal::WireFormatLite::WriteBool(9, this->issystem(), output); + } + + // optional bool isSelfHosted = 10; + if (has_isselfhosted()) { + ::google::protobuf::internal::WireFormatLite::WriteBool(10, this->isselfhosted(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.StackFrame.Data) +} + +::google::protobuf::uint8* StackFrame_Data::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:mozilla.devtools.protobuf.StackFrame.Data) + // optional uint64 id = 1; + if (has_id()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->id(), target); + } + + // optional .mozilla.devtools.protobuf.StackFrame parent = 2; + if (has_parent()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 2, this->parent(), target); + } + + // optional uint32 line = 3; + if (has_line()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(3, this->line(), target); + } + + // optional uint32 column = 4; + if (has_column()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(4, this->column(), target); + } + + // optional bytes source = 5; + if (has_source()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 5, this->source(), target); + } + + // optional uint64 sourceRef = 6; + if (has_sourceref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(6, this->sourceref(), target); + } + + // optional bytes functionDisplayName = 7; + if (has_functiondisplayname()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 7, this->functiondisplayname(), target); + } + + // optional uint64 functionDisplayNameRef = 8; + if (has_functiondisplaynameref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(8, this->functiondisplaynameref(), target); + } + + // optional bool isSystem = 9; + if (has_issystem()) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(9, this->issystem(), target); + } + + // optional bool isSelfHosted = 10; + if (has_isselfhosted()) { + target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(10, this->isselfhosted(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:mozilla.devtools.protobuf.StackFrame.Data) + return target; +} + +int StackFrame_Data::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint64 id = 1; + if (has_id()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->id()); + } + + // optional .mozilla.devtools.protobuf.StackFrame parent = 2; + if (has_parent()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->parent()); + } + + // optional uint32 line = 3; + if (has_line()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->line()); + } + + // optional uint32 column = 4; + if (has_column()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->column()); + } + + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + // optional bool isSystem = 9; + if (has_issystem()) { + total_size += 1 + 1; + } + + // optional bool isSelfHosted = 10; + if (has_isselfhosted()) { + total_size += 1 + 1; + } + + } + switch (SourceOrRef_case()) { + // optional bytes source = 5; + case kSource: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->source()); + break; + } + // optional uint64 sourceRef = 6; + case kSourceRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->sourceref()); + break; + } + case SOURCEORREF_NOT_SET: { + break; + } + } + switch (FunctionDisplayNameOrRef_case()) { + // optional bytes functionDisplayName = 7; + case kFunctionDisplayName: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->functiondisplayname()); + break; + } + // optional uint64 functionDisplayNameRef = 8; + case kFunctionDisplayNameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->functiondisplaynameref()); + break; + } + case FUNCTIONDISPLAYNAMEORREF_NOT_SET: { + break; + } + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void StackFrame_Data::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const StackFrame_Data* source = + ::google::protobuf::internal::dynamic_cast_if_available<const StackFrame_Data*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void StackFrame_Data::MergeFrom(const StackFrame_Data& from) { + GOOGLE_CHECK_NE(&from, this); + switch (from.SourceOrRef_case()) { + case kSource: { + set_source(from.source()); + break; + } + case kSourceRef: { + set_sourceref(from.sourceref()); + break; + } + case SOURCEORREF_NOT_SET: { + break; + } + } + switch (from.FunctionDisplayNameOrRef_case()) { + case kFunctionDisplayName: { + set_functiondisplayname(from.functiondisplayname()); + break; + } + case kFunctionDisplayNameRef: { + set_functiondisplaynameref(from.functiondisplaynameref()); + break; + } + case FUNCTIONDISPLAYNAMEORREF_NOT_SET: { + break; + } + } + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_id()) { + set_id(from.id()); + } + if (from.has_parent()) { + mutable_parent()->::mozilla::devtools::protobuf::StackFrame::MergeFrom(from.parent()); + } + if (from.has_line()) { + set_line(from.line()); + } + if (from.has_column()) { + set_column(from.column()); + } + } + if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (from.has_issystem()) { + set_issystem(from.issystem()); + } + if (from.has_isselfhosted()) { + set_isselfhosted(from.isselfhosted()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void StackFrame_Data::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void StackFrame_Data::CopyFrom(const StackFrame_Data& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool StackFrame_Data::IsInitialized() const { + + return true; +} + +void StackFrame_Data::Swap(StackFrame_Data* other) { + if (other != this) { + std::swap(id_, other->id_); + std::swap(parent_, other->parent_); + std::swap(line_, other->line_); + std::swap(column_, other->column_); + std::swap(issystem_, other->issystem_); + std::swap(isselfhosted_, other->isselfhosted_); + std::swap(SourceOrRef_, other->SourceOrRef_); + std::swap(_oneof_case_[0], other->_oneof_case_[0]); + std::swap(FunctionDisplayNameOrRef_, other->FunctionDisplayNameOrRef_); + std::swap(_oneof_case_[1], other->_oneof_case_[1]); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata StackFrame_Data::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = StackFrame_Data_descriptor_; + metadata.reflection = StackFrame_Data_reflection_; + return metadata; +} + + +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int StackFrame::kDataFieldNumber; +const int StackFrame::kRefFieldNumber; +#endif // !_MSC_VER + +StackFrame::StackFrame() + : ::google::protobuf::Message() { + SharedCtor(); + // @@protoc_insertion_point(constructor:mozilla.devtools.protobuf.StackFrame) +} + +void StackFrame::InitAsDefaultInstance() { + StackFrame_default_oneof_instance_->data_ = const_cast< ::mozilla::devtools::protobuf::StackFrame_Data*>(&::mozilla::devtools::protobuf::StackFrame_Data::default_instance()); + StackFrame_default_oneof_instance_->ref_ = GOOGLE_ULONGLONG(0); +} + +StackFrame::StackFrame(const StackFrame& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.StackFrame) +} + +void StackFrame::SharedCtor() { + _cached_size_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + clear_has_StackFrameType(); +} + +StackFrame::~StackFrame() { + // @@protoc_insertion_point(destructor:mozilla.devtools.protobuf.StackFrame) + SharedDtor(); +} + +void StackFrame::SharedDtor() { + if (has_StackFrameType()) { + clear_StackFrameType(); + } + if (this != default_instance_) { + } +} + +void StackFrame::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* StackFrame::descriptor() { + protobuf_AssignDescriptorsOnce(); + return StackFrame_descriptor_; +} + +const StackFrame& StackFrame::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_CoreDump_2eproto(); + return *default_instance_; +} + +StackFrame* StackFrame::default_instance_ = NULL; + +StackFrame* StackFrame::New() const { + return new StackFrame; +} + +void StackFrame::clear_StackFrameType() { + switch(StackFrameType_case()) { + case kData: { + delete StackFrameType_.data_; + break; + } + case kRef: { + // No need to clear + break; + } + case STACKFRAMETYPE_NOT_SET: { + break; + } + } + _oneof_case_[0] = STACKFRAMETYPE_NOT_SET; +} + + +void StackFrame::Clear() { + clear_StackFrameType(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool StackFrame::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:mozilla.devtools.protobuf.StackFrame) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; + case 1: { + if (tag == 10) { + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_data())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_ref; + break; + } + + // optional uint64 ref = 2; + case 2: { + if (tag == 16) { + parse_ref: + clear_StackFrameType(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &StackFrameType_.ref_))); + set_has_ref(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:mozilla.devtools.protobuf.StackFrame) + return true; +failure: + // @@protoc_insertion_point(parse_failure:mozilla.devtools.protobuf.StackFrame) + return false; +#undef DO_ +} + +void StackFrame::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:mozilla.devtools.protobuf.StackFrame) + // optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; + if (has_data()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 1, this->data(), output); + } + + // optional uint64 ref = 2; + if (has_ref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(2, this->ref(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.StackFrame) +} + +::google::protobuf::uint8* StackFrame::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:mozilla.devtools.protobuf.StackFrame) + // optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; + if (has_data()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 1, this->data(), target); + } + + // optional uint64 ref = 2; + if (has_ref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(2, this->ref(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:mozilla.devtools.protobuf.StackFrame) + return target; +} + +int StackFrame::ByteSize() const { + int total_size = 0; + + switch (StackFrameType_case()) { + // optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; + case kData: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->data()); + break; + } + // optional uint64 ref = 2; + case kRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->ref()); + break; + } + case STACKFRAMETYPE_NOT_SET: { + break; + } + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void StackFrame::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const StackFrame* source = + ::google::protobuf::internal::dynamic_cast_if_available<const StackFrame*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void StackFrame::MergeFrom(const StackFrame& from) { + GOOGLE_CHECK_NE(&from, this); + switch (from.StackFrameType_case()) { + case kData: { + mutable_data()->::mozilla::devtools::protobuf::StackFrame_Data::MergeFrom(from.data()); + break; + } + case kRef: { + set_ref(from.ref()); + break; + } + case STACKFRAMETYPE_NOT_SET: { + break; + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void StackFrame::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void StackFrame::CopyFrom(const StackFrame& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool StackFrame::IsInitialized() const { + + return true; +} + +void StackFrame::Swap(StackFrame* other) { + if (other != this) { + std::swap(StackFrameType_, other->StackFrameType_); + std::swap(_oneof_case_[0], other->_oneof_case_[0]); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata StackFrame::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = StackFrame_descriptor_; + metadata.reflection = StackFrame_reflection_; + return metadata; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int Node::kIdFieldNumber; +const int Node::kTypeNameFieldNumber; +const int Node::kTypeNameRefFieldNumber; +const int Node::kSizeFieldNumber; +const int Node::kEdgesFieldNumber; +const int Node::kAllocationStackFieldNumber; +const int Node::kJsObjectClassNameFieldNumber; +const int Node::kJsObjectClassNameRefFieldNumber; +const int Node::kCoarseTypeFieldNumber; +const int Node::kScriptFilenameFieldNumber; +const int Node::kScriptFilenameRefFieldNumber; +#endif // !_MSC_VER + +Node::Node() + : ::google::protobuf::Message() { + SharedCtor(); + // @@protoc_insertion_point(constructor:mozilla.devtools.protobuf.Node) +} + +void Node::InitAsDefaultInstance() { + Node_default_oneof_instance_->typename__ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + Node_default_oneof_instance_->typenameref_ = GOOGLE_ULONGLONG(0); + allocationstack_ = const_cast< ::mozilla::devtools::protobuf::StackFrame*>(&::mozilla::devtools::protobuf::StackFrame::default_instance()); + Node_default_oneof_instance_->jsobjectclassname_ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + Node_default_oneof_instance_->jsobjectclassnameref_ = GOOGLE_ULONGLONG(0); + Node_default_oneof_instance_->scriptfilename_ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + Node_default_oneof_instance_->scriptfilenameref_ = GOOGLE_ULONGLONG(0); +} + +Node::Node(const Node& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.Node) +} + +void Node::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + id_ = GOOGLE_ULONGLONG(0); + size_ = GOOGLE_ULONGLONG(0); + allocationstack_ = NULL; + coarsetype_ = 0u; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + clear_has_TypeNameOrRef(); + clear_has_JSObjectClassNameOrRef(); + clear_has_ScriptFilenameOrRef(); +} + +Node::~Node() { + // @@protoc_insertion_point(destructor:mozilla.devtools.protobuf.Node) + SharedDtor(); +} + +void Node::SharedDtor() { + if (has_TypeNameOrRef()) { + clear_TypeNameOrRef(); + } + if (has_JSObjectClassNameOrRef()) { + clear_JSObjectClassNameOrRef(); + } + if (has_ScriptFilenameOrRef()) { + clear_ScriptFilenameOrRef(); + } + if (this != default_instance_) { + delete allocationstack_; + } +} + +void Node::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Node::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Node_descriptor_; +} + +const Node& Node::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_CoreDump_2eproto(); + return *default_instance_; +} + +Node* Node::default_instance_ = NULL; + +Node* Node::New() const { + return new Node; +} + +void Node::clear_TypeNameOrRef() { + switch(TypeNameOrRef_case()) { + case kTypeName: { + delete TypeNameOrRef_.typename__; + break; + } + case kTypeNameRef: { + // No need to clear + break; + } + case TYPENAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[0] = TYPENAMEORREF_NOT_SET; +} + +void Node::clear_JSObjectClassNameOrRef() { + switch(JSObjectClassNameOrRef_case()) { + case kJsObjectClassName: { + delete JSObjectClassNameOrRef_.jsobjectclassname_; + break; + } + case kJsObjectClassNameRef: { + // No need to clear + break; + } + case JSOBJECTCLASSNAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[1] = JSOBJECTCLASSNAMEORREF_NOT_SET; +} + +void Node::clear_ScriptFilenameOrRef() { + switch(ScriptFilenameOrRef_case()) { + case kScriptFilename: { + delete ScriptFilenameOrRef_.scriptfilename_; + break; + } + case kScriptFilenameRef: { + // No need to clear + break; + } + case SCRIPTFILENAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[2] = SCRIPTFILENAMEORREF_NOT_SET; +} + + +void Node::Clear() { +#define OFFSET_OF_FIELD_(f) (reinterpret_cast<char*>( \ + &reinterpret_cast<Node*>(16)->f) - \ + reinterpret_cast<char*>(16)) + +#define ZR_(first, last) do { \ + size_t f = OFFSET_OF_FIELD_(first); \ + size_t n = OFFSET_OF_FIELD_(last) - f + sizeof(last); \ + ::memset(&first, 0, n); \ + } while (0) + + if (_has_bits_[0 / 32] & 41) { + ZR_(id_, size_); + if (has_allocationstack()) { + if (allocationstack_ != NULL) allocationstack_->::mozilla::devtools::protobuf::StackFrame::Clear(); + } + } + coarsetype_ = 0u; + +#undef OFFSET_OF_FIELD_ +#undef ZR_ + + edges_.Clear(); + clear_TypeNameOrRef(); + clear_JSObjectClassNameOrRef(); + clear_ScriptFilenameOrRef(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Node::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:mozilla.devtools.protobuf.Node) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint64 id = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &id_))); + set_has_id(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_typeName; + break; + } + + // optional bytes typeName = 2; + case 2: { + if (tag == 18) { + parse_typeName: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_typename_())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(24)) goto parse_typeNameRef; + break; + } + + // optional uint64 typeNameRef = 3; + case 3: { + if (tag == 24) { + parse_typeNameRef: + clear_TypeNameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &TypeNameOrRef_.typenameref_))); + set_has_typenameref(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(32)) goto parse_size; + break; + } + + // optional uint64 size = 4; + case 4: { + if (tag == 32) { + parse_size: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &size_))); + set_has_size(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(42)) goto parse_edges; + break; + } + + // repeated .mozilla.devtools.protobuf.Edge edges = 5; + case 5: { + if (tag == 42) { + parse_edges: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, add_edges())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(42)) goto parse_edges; + if (input->ExpectTag(50)) goto parse_allocationStack; + break; + } + + // optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; + case 6: { + if (tag == 50) { + parse_allocationStack: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + input, mutable_allocationstack())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(58)) goto parse_jsObjectClassName; + break; + } + + // optional bytes jsObjectClassName = 7; + case 7: { + if (tag == 58) { + parse_jsObjectClassName: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_jsobjectclassname())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(64)) goto parse_jsObjectClassNameRef; + break; + } + + // optional uint64 jsObjectClassNameRef = 8; + case 8: { + if (tag == 64) { + parse_jsObjectClassNameRef: + clear_JSObjectClassNameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &JSObjectClassNameOrRef_.jsobjectclassnameref_))); + set_has_jsobjectclassnameref(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(72)) goto parse_coarseType; + break; + } + + // optional uint32 coarseType = 9 [default = 0]; + case 9: { + if (tag == 72) { + parse_coarseType: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>( + input, &coarsetype_))); + set_has_coarsetype(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(82)) goto parse_scriptFilename; + break; + } + + // optional bytes scriptFilename = 10; + case 10: { + if (tag == 82) { + parse_scriptFilename: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_scriptfilename())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(88)) goto parse_scriptFilenameRef; + break; + } + + // optional uint64 scriptFilenameRef = 11; + case 11: { + if (tag == 88) { + parse_scriptFilenameRef: + clear_ScriptFilenameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &ScriptFilenameOrRef_.scriptfilenameref_))); + set_has_scriptfilenameref(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:mozilla.devtools.protobuf.Node) + return true; +failure: + // @@protoc_insertion_point(parse_failure:mozilla.devtools.protobuf.Node) + return false; +#undef DO_ +} + +void Node::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:mozilla.devtools.protobuf.Node) + // optional uint64 id = 1; + if (has_id()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->id(), output); + } + + // optional bytes typeName = 2; + if (has_typename_()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 2, this->typename_(), output); + } + + // optional uint64 typeNameRef = 3; + if (has_typenameref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(3, this->typenameref(), output); + } + + // optional uint64 size = 4; + if (has_size()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(4, this->size(), output); + } + + // repeated .mozilla.devtools.protobuf.Edge edges = 5; + for (int i = 0; i < this->edges_size(); i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 5, this->edges(i), output); + } + + // optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; + if (has_allocationstack()) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 6, this->allocationstack(), output); + } + + // optional bytes jsObjectClassName = 7; + if (has_jsobjectclassname()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 7, this->jsobjectclassname(), output); + } + + // optional uint64 jsObjectClassNameRef = 8; + if (has_jsobjectclassnameref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(8, this->jsobjectclassnameref(), output); + } + + // optional uint32 coarseType = 9 [default = 0]; + if (has_coarsetype()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt32(9, this->coarsetype(), output); + } + + // optional bytes scriptFilename = 10; + if (has_scriptfilename()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 10, this->scriptfilename(), output); + } + + // optional uint64 scriptFilenameRef = 11; + if (has_scriptfilenameref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(11, this->scriptfilenameref(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.Node) +} + +::google::protobuf::uint8* Node::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:mozilla.devtools.protobuf.Node) + // optional uint64 id = 1; + if (has_id()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->id(), target); + } + + // optional bytes typeName = 2; + if (has_typename_()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 2, this->typename_(), target); + } + + // optional uint64 typeNameRef = 3; + if (has_typenameref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(3, this->typenameref(), target); + } + + // optional uint64 size = 4; + if (has_size()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(4, this->size(), target); + } + + // repeated .mozilla.devtools.protobuf.Edge edges = 5; + for (int i = 0; i < this->edges_size(); i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 5, this->edges(i), target); + } + + // optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; + if (has_allocationstack()) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 6, this->allocationstack(), target); + } + + // optional bytes jsObjectClassName = 7; + if (has_jsobjectclassname()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 7, this->jsobjectclassname(), target); + } + + // optional uint64 jsObjectClassNameRef = 8; + if (has_jsobjectclassnameref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(8, this->jsobjectclassnameref(), target); + } + + // optional uint32 coarseType = 9 [default = 0]; + if (has_coarsetype()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(9, this->coarsetype(), target); + } + + // optional bytes scriptFilename = 10; + if (has_scriptfilename()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 10, this->scriptfilename(), target); + } + + // optional uint64 scriptFilenameRef = 11; + if (has_scriptfilenameref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(11, this->scriptfilenameref(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:mozilla.devtools.protobuf.Node) + return target; +} + +int Node::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint64 id = 1; + if (has_id()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->id()); + } + + // optional uint64 size = 4; + if (has_size()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->size()); + } + + // optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; + if (has_allocationstack()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->allocationstack()); + } + + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + // optional uint32 coarseType = 9 [default = 0]; + if (has_coarsetype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt32Size( + this->coarsetype()); + } + + } + // repeated .mozilla.devtools.protobuf.Edge edges = 5; + total_size += 1 * this->edges_size(); + for (int i = 0; i < this->edges_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->edges(i)); + } + + switch (TypeNameOrRef_case()) { + // optional bytes typeName = 2; + case kTypeName: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->typename_()); + break; + } + // optional uint64 typeNameRef = 3; + case kTypeNameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->typenameref()); + break; + } + case TYPENAMEORREF_NOT_SET: { + break; + } + } + switch (JSObjectClassNameOrRef_case()) { + // optional bytes jsObjectClassName = 7; + case kJsObjectClassName: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->jsobjectclassname()); + break; + } + // optional uint64 jsObjectClassNameRef = 8; + case kJsObjectClassNameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->jsobjectclassnameref()); + break; + } + case JSOBJECTCLASSNAMEORREF_NOT_SET: { + break; + } + } + switch (ScriptFilenameOrRef_case()) { + // optional bytes scriptFilename = 10; + case kScriptFilename: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->scriptfilename()); + break; + } + // optional uint64 scriptFilenameRef = 11; + case kScriptFilenameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->scriptfilenameref()); + break; + } + case SCRIPTFILENAMEORREF_NOT_SET: { + break; + } + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Node::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Node* source = + ::google::protobuf::internal::dynamic_cast_if_available<const Node*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Node::MergeFrom(const Node& from) { + GOOGLE_CHECK_NE(&from, this); + edges_.MergeFrom(from.edges_); + switch (from.TypeNameOrRef_case()) { + case kTypeName: { + set_typename_(from.typename_()); + break; + } + case kTypeNameRef: { + set_typenameref(from.typenameref()); + break; + } + case TYPENAMEORREF_NOT_SET: { + break; + } + } + switch (from.JSObjectClassNameOrRef_case()) { + case kJsObjectClassName: { + set_jsobjectclassname(from.jsobjectclassname()); + break; + } + case kJsObjectClassNameRef: { + set_jsobjectclassnameref(from.jsobjectclassnameref()); + break; + } + case JSOBJECTCLASSNAMEORREF_NOT_SET: { + break; + } + } + switch (from.ScriptFilenameOrRef_case()) { + case kScriptFilename: { + set_scriptfilename(from.scriptfilename()); + break; + } + case kScriptFilenameRef: { + set_scriptfilenameref(from.scriptfilenameref()); + break; + } + case SCRIPTFILENAMEORREF_NOT_SET: { + break; + } + } + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_id()) { + set_id(from.id()); + } + if (from.has_size()) { + set_size(from.size()); + } + if (from.has_allocationstack()) { + mutable_allocationstack()->::mozilla::devtools::protobuf::StackFrame::MergeFrom(from.allocationstack()); + } + } + if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (from.has_coarsetype()) { + set_coarsetype(from.coarsetype()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Node::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Node::CopyFrom(const Node& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Node::IsInitialized() const { + + return true; +} + +void Node::Swap(Node* other) { + if (other != this) { + std::swap(id_, other->id_); + std::swap(size_, other->size_); + edges_.Swap(&other->edges_); + std::swap(allocationstack_, other->allocationstack_); + std::swap(coarsetype_, other->coarsetype_); + std::swap(TypeNameOrRef_, other->TypeNameOrRef_); + std::swap(_oneof_case_[0], other->_oneof_case_[0]); + std::swap(JSObjectClassNameOrRef_, other->JSObjectClassNameOrRef_); + std::swap(_oneof_case_[1], other->_oneof_case_[1]); + std::swap(ScriptFilenameOrRef_, other->ScriptFilenameOrRef_); + std::swap(_oneof_case_[2], other->_oneof_case_[2]); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Node::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Node_descriptor_; + metadata.reflection = Node_reflection_; + return metadata; +} + + +// =================================================================== + +#ifndef _MSC_VER +const int Edge::kReferentFieldNumber; +const int Edge::kNameFieldNumber; +const int Edge::kNameRefFieldNumber; +#endif // !_MSC_VER + +Edge::Edge() + : ::google::protobuf::Message() { + SharedCtor(); + // @@protoc_insertion_point(constructor:mozilla.devtools.protobuf.Edge) +} + +void Edge::InitAsDefaultInstance() { + Edge_default_oneof_instance_->name_ = &::google::protobuf::internal::GetEmptyStringAlreadyInited(); + Edge_default_oneof_instance_->nameref_ = GOOGLE_ULONGLONG(0); +} + +Edge::Edge(const Edge& from) + : ::google::protobuf::Message() { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.Edge) +} + +void Edge::SharedCtor() { + ::google::protobuf::internal::GetEmptyString(); + _cached_size_ = 0; + referent_ = GOOGLE_ULONGLONG(0); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + clear_has_EdgeNameOrRef(); +} + +Edge::~Edge() { + // @@protoc_insertion_point(destructor:mozilla.devtools.protobuf.Edge) + SharedDtor(); +} + +void Edge::SharedDtor() { + if (has_EdgeNameOrRef()) { + clear_EdgeNameOrRef(); + } + if (this != default_instance_) { + } +} + +void Edge::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* Edge::descriptor() { + protobuf_AssignDescriptorsOnce(); + return Edge_descriptor_; +} + +const Edge& Edge::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_CoreDump_2eproto(); + return *default_instance_; +} + +Edge* Edge::default_instance_ = NULL; + +Edge* Edge::New() const { + return new Edge; +} + +void Edge::clear_EdgeNameOrRef() { + switch(EdgeNameOrRef_case()) { + case kName: { + delete EdgeNameOrRef_.name_; + break; + } + case kNameRef: { + // No need to clear + break; + } + case EDGENAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[0] = EDGENAMEORREF_NOT_SET; +} + + +void Edge::Clear() { + referent_ = GOOGLE_ULONGLONG(0); + clear_EdgeNameOrRef(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Edge::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:mozilla.devtools.protobuf.Edge) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional uint64 referent = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &referent_))); + set_has_referent(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_name; + break; + } + + // optional bytes name = 2; + case 2: { + if (tag == 18) { + parse_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_name())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(24)) goto parse_nameRef; + break; + } + + // optional uint64 nameRef = 3; + case 3: { + if (tag == 24) { + parse_nameRef: + clear_EdgeNameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &EdgeNameOrRef_.nameref_))); + set_has_nameref(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:mozilla.devtools.protobuf.Edge) + return true; +failure: + // @@protoc_insertion_point(parse_failure:mozilla.devtools.protobuf.Edge) + return false; +#undef DO_ +} + +void Edge::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:mozilla.devtools.protobuf.Edge) + // optional uint64 referent = 1; + if (has_referent()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(1, this->referent(), output); + } + + // optional bytes name = 2; + if (has_name()) { + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 2, this->name(), output); + } + + // optional uint64 nameRef = 3; + if (has_nameref()) { + ::google::protobuf::internal::WireFormatLite::WriteUInt64(3, this->nameref(), output); + } + + if (!unknown_fields().empty()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.Edge) +} + +::google::protobuf::uint8* Edge::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:mozilla.devtools.protobuf.Edge) + // optional uint64 referent = 1; + if (has_referent()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(1, this->referent(), target); + } + + // optional bytes name = 2; + if (has_name()) { + target = + ::google::protobuf::internal::WireFormatLite::WriteBytesToArray( + 2, this->name(), target); + } + + // optional uint64 nameRef = 3; + if (has_nameref()) { + target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(3, this->nameref(), target); + } + + if (!unknown_fields().empty()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:mozilla.devtools.protobuf.Edge) + return target; +} + +int Edge::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional uint64 referent = 1; + if (has_referent()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->referent()); + } + + } + switch (EdgeNameOrRef_case()) { + // optional bytes name = 2; + case kName: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->name()); + break; + } + // optional uint64 nameRef = 3; + case kNameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->nameref()); + break; + } + case EDGENAMEORREF_NOT_SET: { + break; + } + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void Edge::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Edge* source = + ::google::protobuf::internal::dynamic_cast_if_available<const Edge*>( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Edge::MergeFrom(const Edge& from) { + GOOGLE_CHECK_NE(&from, this); + switch (from.EdgeNameOrRef_case()) { + case kName: { + set_name(from.name()); + break; + } + case kNameRef: { + set_nameref(from.nameref()); + break; + } + case EDGENAMEORREF_NOT_SET: { + break; + } + } + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_referent()) { + set_referent(from.referent()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Edge::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Edge::CopyFrom(const Edge& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Edge::IsInitialized() const { + + return true; +} + +void Edge::Swap(Edge* other) { + if (other != this) { + std::swap(referent_, other->referent_); + std::swap(EdgeNameOrRef_, other->EdgeNameOrRef_); + std::swap(_oneof_case_[0], other->_oneof_case_[0]); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +::google::protobuf::Metadata Edge::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = Edge_descriptor_; + metadata.reflection = Edge_reflection_; + return metadata; +} + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace protobuf +} // namespace devtools +} // namespace mozilla + +// @@protoc_insertion_point(global_scope) diff --git a/dom/heapsnapshot/CoreDump.pb.h b/dom/heapsnapshot/CoreDump.pb.h new file mode 100644 index 000000000..584c2e379 --- /dev/null +++ b/dom/heapsnapshot/CoreDump.pb.h @@ -0,0 +1,1893 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: CoreDump.proto + +#ifndef PROTOBUF_CoreDump_2eproto__INCLUDED +#define PROTOBUF_CoreDump_2eproto__INCLUDED + +#include <string> + +#include <google/protobuf/stubs/common.h> + +#if GOOGLE_PROTOBUF_VERSION < 2006000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include <google/protobuf/generated_message_util.h> +#include <google/protobuf/message.h> +#include <google/protobuf/repeated_field.h> +#include <google/protobuf/extension_set.h> +#include <google/protobuf/unknown_field_set.h> +// @@protoc_insertion_point(includes) + +namespace mozilla { +namespace devtools { +namespace protobuf { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_CoreDump_2eproto(); +void protobuf_AssignDesc_CoreDump_2eproto(); +void protobuf_ShutdownFile_CoreDump_2eproto(); + +class Metadata; +class StackFrame; +class StackFrame_Data; +class Node; +class Edge; + +// =================================================================== + +class Metadata : public ::google::protobuf::Message { + public: + Metadata(); + virtual ~Metadata(); + + Metadata(const Metadata& from); + + inline Metadata& operator=(const Metadata& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Metadata& default_instance(); + + void Swap(Metadata* other); + + // implements Message ---------------------------------------------- + + Metadata* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Metadata& from); + void MergeFrom(const Metadata& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint64 timeStamp = 1; + inline bool has_timestamp() const; + inline void clear_timestamp(); + static const int kTimeStampFieldNumber = 1; + inline ::google::protobuf::uint64 timestamp() const; + inline void set_timestamp(::google::protobuf::uint64 value); + + // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Metadata) + private: + inline void set_has_timestamp(); + inline void clear_has_timestamp(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + ::google::protobuf::uint64 timestamp_; + friend void protobuf_AddDesc_CoreDump_2eproto(); + friend void protobuf_AssignDesc_CoreDump_2eproto(); + friend void protobuf_ShutdownFile_CoreDump_2eproto(); + + void InitAsDefaultInstance(); + static Metadata* default_instance_; +}; +// ------------------------------------------------------------------- + +class StackFrame_Data : public ::google::protobuf::Message { + public: + StackFrame_Data(); + virtual ~StackFrame_Data(); + + StackFrame_Data(const StackFrame_Data& from); + + inline StackFrame_Data& operator=(const StackFrame_Data& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const StackFrame_Data& default_instance(); + + enum SourceOrRefCase { + kSource = 5, + kSourceRef = 6, + SOURCEORREF_NOT_SET = 0, + }; + + enum FunctionDisplayNameOrRefCase { + kFunctionDisplayName = 7, + kFunctionDisplayNameRef = 8, + FUNCTIONDISPLAYNAMEORREF_NOT_SET = 0, + }; + + void Swap(StackFrame_Data* other); + + // implements Message ---------------------------------------------- + + StackFrame_Data* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const StackFrame_Data& from); + void MergeFrom(const StackFrame_Data& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint64 id = 1; + inline bool has_id() const; + inline void clear_id(); + static const int kIdFieldNumber = 1; + inline ::google::protobuf::uint64 id() const; + inline void set_id(::google::protobuf::uint64 value); + + // optional .mozilla.devtools.protobuf.StackFrame parent = 2; + inline bool has_parent() const; + inline void clear_parent(); + static const int kParentFieldNumber = 2; + inline const ::mozilla::devtools::protobuf::StackFrame& parent() const; + inline ::mozilla::devtools::protobuf::StackFrame* mutable_parent(); + inline ::mozilla::devtools::protobuf::StackFrame* release_parent(); + inline void set_allocated_parent(::mozilla::devtools::protobuf::StackFrame* parent); + + // optional uint32 line = 3; + inline bool has_line() const; + inline void clear_line(); + static const int kLineFieldNumber = 3; + inline ::google::protobuf::uint32 line() const; + inline void set_line(::google::protobuf::uint32 value); + + // optional uint32 column = 4; + inline bool has_column() const; + inline void clear_column(); + static const int kColumnFieldNumber = 4; + inline ::google::protobuf::uint32 column() const; + inline void set_column(::google::protobuf::uint32 value); + + // optional bytes source = 5; + inline bool has_source() const; + inline void clear_source(); + static const int kSourceFieldNumber = 5; + inline const ::std::string& source() const; + inline void set_source(const ::std::string& value); + inline void set_source(const char* value); + inline void set_source(const void* value, size_t size); + inline ::std::string* mutable_source(); + inline ::std::string* release_source(); + inline void set_allocated_source(::std::string* source); + + // optional uint64 sourceRef = 6; + inline bool has_sourceref() const; + inline void clear_sourceref(); + static const int kSourceRefFieldNumber = 6; + inline ::google::protobuf::uint64 sourceref() const; + inline void set_sourceref(::google::protobuf::uint64 value); + + // optional bytes functionDisplayName = 7; + inline bool has_functiondisplayname() const; + inline void clear_functiondisplayname(); + static const int kFunctionDisplayNameFieldNumber = 7; + inline const ::std::string& functiondisplayname() const; + inline void set_functiondisplayname(const ::std::string& value); + inline void set_functiondisplayname(const char* value); + inline void set_functiondisplayname(const void* value, size_t size); + inline ::std::string* mutable_functiondisplayname(); + inline ::std::string* release_functiondisplayname(); + inline void set_allocated_functiondisplayname(::std::string* functiondisplayname); + + // optional uint64 functionDisplayNameRef = 8; + inline bool has_functiondisplaynameref() const; + inline void clear_functiondisplaynameref(); + static const int kFunctionDisplayNameRefFieldNumber = 8; + inline ::google::protobuf::uint64 functiondisplaynameref() const; + inline void set_functiondisplaynameref(::google::protobuf::uint64 value); + + // optional bool isSystem = 9; + inline bool has_issystem() const; + inline void clear_issystem(); + static const int kIsSystemFieldNumber = 9; + inline bool issystem() const; + inline void set_issystem(bool value); + + // optional bool isSelfHosted = 10; + inline bool has_isselfhosted() const; + inline void clear_isselfhosted(); + static const int kIsSelfHostedFieldNumber = 10; + inline bool isselfhosted() const; + inline void set_isselfhosted(bool value); + + inline SourceOrRefCase SourceOrRef_case() const; + inline FunctionDisplayNameOrRefCase FunctionDisplayNameOrRef_case() const; + // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.StackFrame.Data) + private: + inline void set_has_id(); + inline void clear_has_id(); + inline void set_has_parent(); + inline void clear_has_parent(); + inline void set_has_line(); + inline void clear_has_line(); + inline void set_has_column(); + inline void clear_has_column(); + inline void set_has_source(); + inline void set_has_sourceref(); + inline void set_has_functiondisplayname(); + inline void set_has_functiondisplaynameref(); + inline void set_has_issystem(); + inline void clear_has_issystem(); + inline void set_has_isselfhosted(); + inline void clear_has_isselfhosted(); + + inline bool has_SourceOrRef(); + void clear_SourceOrRef(); + inline void clear_has_SourceOrRef(); + + inline bool has_FunctionDisplayNameOrRef(); + void clear_FunctionDisplayNameOrRef(); + inline void clear_has_FunctionDisplayNameOrRef(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + ::google::protobuf::uint64 id_; + ::mozilla::devtools::protobuf::StackFrame* parent_; + ::google::protobuf::uint32 line_; + ::google::protobuf::uint32 column_; + bool issystem_; + bool isselfhosted_; + union SourceOrRefUnion { + ::std::string* source_; + ::google::protobuf::uint64 sourceref_; + } SourceOrRef_; + union FunctionDisplayNameOrRefUnion { + ::std::string* functiondisplayname_; + ::google::protobuf::uint64 functiondisplaynameref_; + } FunctionDisplayNameOrRef_; + ::google::protobuf::uint32 _oneof_case_[2]; + + friend void protobuf_AddDesc_CoreDump_2eproto(); + friend void protobuf_AssignDesc_CoreDump_2eproto(); + friend void protobuf_ShutdownFile_CoreDump_2eproto(); + + void InitAsDefaultInstance(); + static StackFrame_Data* default_instance_; +}; +// ------------------------------------------------------------------- + +class StackFrame : public ::google::protobuf::Message { + public: + StackFrame(); + virtual ~StackFrame(); + + StackFrame(const StackFrame& from); + + inline StackFrame& operator=(const StackFrame& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const StackFrame& default_instance(); + + enum StackFrameTypeCase { + kData = 1, + kRef = 2, + STACKFRAMETYPE_NOT_SET = 0, + }; + + void Swap(StackFrame* other); + + // implements Message ---------------------------------------------- + + StackFrame* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const StackFrame& from); + void MergeFrom(const StackFrame& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + typedef StackFrame_Data Data; + + // accessors ------------------------------------------------------- + + // optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; + inline bool has_data() const; + inline void clear_data(); + static const int kDataFieldNumber = 1; + inline const ::mozilla::devtools::protobuf::StackFrame_Data& data() const; + inline ::mozilla::devtools::protobuf::StackFrame_Data* mutable_data(); + inline ::mozilla::devtools::protobuf::StackFrame_Data* release_data(); + inline void set_allocated_data(::mozilla::devtools::protobuf::StackFrame_Data* data); + + // optional uint64 ref = 2; + inline bool has_ref() const; + inline void clear_ref(); + static const int kRefFieldNumber = 2; + inline ::google::protobuf::uint64 ref() const; + inline void set_ref(::google::protobuf::uint64 value); + + inline StackFrameTypeCase StackFrameType_case() const; + // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.StackFrame) + private: + inline void set_has_data(); + inline void set_has_ref(); + + inline bool has_StackFrameType(); + void clear_StackFrameType(); + inline void clear_has_StackFrameType(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + union StackFrameTypeUnion { + ::mozilla::devtools::protobuf::StackFrame_Data* data_; + ::google::protobuf::uint64 ref_; + } StackFrameType_; + ::google::protobuf::uint32 _oneof_case_[1]; + + friend void protobuf_AddDesc_CoreDump_2eproto(); + friend void protobuf_AssignDesc_CoreDump_2eproto(); + friend void protobuf_ShutdownFile_CoreDump_2eproto(); + + void InitAsDefaultInstance(); + static StackFrame* default_instance_; +}; +// ------------------------------------------------------------------- + +class Node : public ::google::protobuf::Message { + public: + Node(); + virtual ~Node(); + + Node(const Node& from); + + inline Node& operator=(const Node& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Node& default_instance(); + + enum TypeNameOrRefCase { + kTypeName = 2, + kTypeNameRef = 3, + TYPENAMEORREF_NOT_SET = 0, + }; + + enum JSObjectClassNameOrRefCase { + kJsObjectClassName = 7, + kJsObjectClassNameRef = 8, + JSOBJECTCLASSNAMEORREF_NOT_SET = 0, + }; + + enum ScriptFilenameOrRefCase { + kScriptFilename = 10, + kScriptFilenameRef = 11, + SCRIPTFILENAMEORREF_NOT_SET = 0, + }; + + void Swap(Node* other); + + // implements Message ---------------------------------------------- + + Node* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Node& from); + void MergeFrom(const Node& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint64 id = 1; + inline bool has_id() const; + inline void clear_id(); + static const int kIdFieldNumber = 1; + inline ::google::protobuf::uint64 id() const; + inline void set_id(::google::protobuf::uint64 value); + + // optional bytes typeName = 2; + inline bool has_typename_() const; + inline void clear_typename_(); + static const int kTypeNameFieldNumber = 2; + inline const ::std::string& typename_() const; + inline void set_typename_(const ::std::string& value); + inline void set_typename_(const char* value); + inline void set_typename_(const void* value, size_t size); + inline ::std::string* mutable_typename_(); + inline ::std::string* release_typename_(); + inline void set_allocated_typename_(::std::string* typename_); + + // optional uint64 typeNameRef = 3; + inline bool has_typenameref() const; + inline void clear_typenameref(); + static const int kTypeNameRefFieldNumber = 3; + inline ::google::protobuf::uint64 typenameref() const; + inline void set_typenameref(::google::protobuf::uint64 value); + + // optional uint64 size = 4; + inline bool has_size() const; + inline void clear_size(); + static const int kSizeFieldNumber = 4; + inline ::google::protobuf::uint64 size() const; + inline void set_size(::google::protobuf::uint64 value); + + // repeated .mozilla.devtools.protobuf.Edge edges = 5; + inline int edges_size() const; + inline void clear_edges(); + static const int kEdgesFieldNumber = 5; + inline const ::mozilla::devtools::protobuf::Edge& edges(int index) const; + inline ::mozilla::devtools::protobuf::Edge* mutable_edges(int index); + inline ::mozilla::devtools::protobuf::Edge* add_edges(); + inline const ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >& + edges() const; + inline ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >* + mutable_edges(); + + // optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; + inline bool has_allocationstack() const; + inline void clear_allocationstack(); + static const int kAllocationStackFieldNumber = 6; + inline const ::mozilla::devtools::protobuf::StackFrame& allocationstack() const; + inline ::mozilla::devtools::protobuf::StackFrame* mutable_allocationstack(); + inline ::mozilla::devtools::protobuf::StackFrame* release_allocationstack(); + inline void set_allocated_allocationstack(::mozilla::devtools::protobuf::StackFrame* allocationstack); + + // optional bytes jsObjectClassName = 7; + inline bool has_jsobjectclassname() const; + inline void clear_jsobjectclassname(); + static const int kJsObjectClassNameFieldNumber = 7; + inline const ::std::string& jsobjectclassname() const; + inline void set_jsobjectclassname(const ::std::string& value); + inline void set_jsobjectclassname(const char* value); + inline void set_jsobjectclassname(const void* value, size_t size); + inline ::std::string* mutable_jsobjectclassname(); + inline ::std::string* release_jsobjectclassname(); + inline void set_allocated_jsobjectclassname(::std::string* jsobjectclassname); + + // optional uint64 jsObjectClassNameRef = 8; + inline bool has_jsobjectclassnameref() const; + inline void clear_jsobjectclassnameref(); + static const int kJsObjectClassNameRefFieldNumber = 8; + inline ::google::protobuf::uint64 jsobjectclassnameref() const; + inline void set_jsobjectclassnameref(::google::protobuf::uint64 value); + + // optional uint32 coarseType = 9 [default = 0]; + inline bool has_coarsetype() const; + inline void clear_coarsetype(); + static const int kCoarseTypeFieldNumber = 9; + inline ::google::protobuf::uint32 coarsetype() const; + inline void set_coarsetype(::google::protobuf::uint32 value); + + // optional bytes scriptFilename = 10; + inline bool has_scriptfilename() const; + inline void clear_scriptfilename(); + static const int kScriptFilenameFieldNumber = 10; + inline const ::std::string& scriptfilename() const; + inline void set_scriptfilename(const ::std::string& value); + inline void set_scriptfilename(const char* value); + inline void set_scriptfilename(const void* value, size_t size); + inline ::std::string* mutable_scriptfilename(); + inline ::std::string* release_scriptfilename(); + inline void set_allocated_scriptfilename(::std::string* scriptfilename); + + // optional uint64 scriptFilenameRef = 11; + inline bool has_scriptfilenameref() const; + inline void clear_scriptfilenameref(); + static const int kScriptFilenameRefFieldNumber = 11; + inline ::google::protobuf::uint64 scriptfilenameref() const; + inline void set_scriptfilenameref(::google::protobuf::uint64 value); + + inline TypeNameOrRefCase TypeNameOrRef_case() const; + inline JSObjectClassNameOrRefCase JSObjectClassNameOrRef_case() const; + inline ScriptFilenameOrRefCase ScriptFilenameOrRef_case() const; + // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Node) + private: + inline void set_has_id(); + inline void clear_has_id(); + inline void set_has_typename_(); + inline void set_has_typenameref(); + inline void set_has_size(); + inline void clear_has_size(); + inline void set_has_allocationstack(); + inline void clear_has_allocationstack(); + inline void set_has_jsobjectclassname(); + inline void set_has_jsobjectclassnameref(); + inline void set_has_coarsetype(); + inline void clear_has_coarsetype(); + inline void set_has_scriptfilename(); + inline void set_has_scriptfilenameref(); + + inline bool has_TypeNameOrRef(); + void clear_TypeNameOrRef(); + inline void clear_has_TypeNameOrRef(); + + inline bool has_JSObjectClassNameOrRef(); + void clear_JSObjectClassNameOrRef(); + inline void clear_has_JSObjectClassNameOrRef(); + + inline bool has_ScriptFilenameOrRef(); + void clear_ScriptFilenameOrRef(); + inline void clear_has_ScriptFilenameOrRef(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + ::google::protobuf::uint64 id_; + ::google::protobuf::uint64 size_; + ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge > edges_; + ::mozilla::devtools::protobuf::StackFrame* allocationstack_; + ::google::protobuf::uint32 coarsetype_; + union TypeNameOrRefUnion { + ::std::string* typename__; + ::google::protobuf::uint64 typenameref_; + } TypeNameOrRef_; + union JSObjectClassNameOrRefUnion { + ::std::string* jsobjectclassname_; + ::google::protobuf::uint64 jsobjectclassnameref_; + } JSObjectClassNameOrRef_; + union ScriptFilenameOrRefUnion { + ::std::string* scriptfilename_; + ::google::protobuf::uint64 scriptfilenameref_; + } ScriptFilenameOrRef_; + ::google::protobuf::uint32 _oneof_case_[3]; + + friend void protobuf_AddDesc_CoreDump_2eproto(); + friend void protobuf_AssignDesc_CoreDump_2eproto(); + friend void protobuf_ShutdownFile_CoreDump_2eproto(); + + void InitAsDefaultInstance(); + static Node* default_instance_; +}; +// ------------------------------------------------------------------- + +class Edge : public ::google::protobuf::Message { + public: + Edge(); + virtual ~Edge(); + + Edge(const Edge& from); + + inline Edge& operator=(const Edge& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Edge& default_instance(); + + enum EdgeNameOrRefCase { + kName = 2, + kNameRef = 3, + EDGENAMEORREF_NOT_SET = 0, + }; + + void Swap(Edge* other); + + // implements Message ---------------------------------------------- + + Edge* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Edge& from); + void MergeFrom(const Edge& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + public: + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional uint64 referent = 1; + inline bool has_referent() const; + inline void clear_referent(); + static const int kReferentFieldNumber = 1; + inline ::google::protobuf::uint64 referent() const; + inline void set_referent(::google::protobuf::uint64 value); + + // optional bytes name = 2; + inline bool has_name() const; + inline void clear_name(); + static const int kNameFieldNumber = 2; + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline void set_name(const void* value, size_t size); + inline ::std::string* mutable_name(); + inline ::std::string* release_name(); + inline void set_allocated_name(::std::string* name); + + // optional uint64 nameRef = 3; + inline bool has_nameref() const; + inline void clear_nameref(); + static const int kNameRefFieldNumber = 3; + inline ::google::protobuf::uint64 nameref() const; + inline void set_nameref(::google::protobuf::uint64 value); + + inline EdgeNameOrRefCase EdgeNameOrRef_case() const; + // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Edge) + private: + inline void set_has_referent(); + inline void clear_has_referent(); + inline void set_has_name(); + inline void set_has_nameref(); + + inline bool has_EdgeNameOrRef(); + void clear_EdgeNameOrRef(); + inline void clear_has_EdgeNameOrRef(); + + ::google::protobuf::UnknownFieldSet _unknown_fields_; + + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + ::google::protobuf::uint64 referent_; + union EdgeNameOrRefUnion { + ::std::string* name_; + ::google::protobuf::uint64 nameref_; + } EdgeNameOrRef_; + ::google::protobuf::uint32 _oneof_case_[1]; + + friend void protobuf_AddDesc_CoreDump_2eproto(); + friend void protobuf_AssignDesc_CoreDump_2eproto(); + friend void protobuf_ShutdownFile_CoreDump_2eproto(); + + void InitAsDefaultInstance(); + static Edge* default_instance_; +}; +// =================================================================== + + +// =================================================================== + +// Metadata + +// optional uint64 timeStamp = 1; +inline bool Metadata::has_timestamp() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Metadata::set_has_timestamp() { + _has_bits_[0] |= 0x00000001u; +} +inline void Metadata::clear_has_timestamp() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Metadata::clear_timestamp() { + timestamp_ = GOOGLE_ULONGLONG(0); + clear_has_timestamp(); +} +inline ::google::protobuf::uint64 Metadata::timestamp() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Metadata.timeStamp) + return timestamp_; +} +inline void Metadata::set_timestamp(::google::protobuf::uint64 value) { + set_has_timestamp(); + timestamp_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Metadata.timeStamp) +} + +// ------------------------------------------------------------------- + +// StackFrame_Data + +// optional uint64 id = 1; +inline bool StackFrame_Data::has_id() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void StackFrame_Data::set_has_id() { + _has_bits_[0] |= 0x00000001u; +} +inline void StackFrame_Data::clear_has_id() { + _has_bits_[0] &= ~0x00000001u; +} +inline void StackFrame_Data::clear_id() { + id_ = GOOGLE_ULONGLONG(0); + clear_has_id(); +} +inline ::google::protobuf::uint64 StackFrame_Data::id() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.id) + return id_; +} +inline void StackFrame_Data::set_id(::google::protobuf::uint64 value) { + set_has_id(); + id_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.StackFrame.Data.id) +} + +// optional .mozilla.devtools.protobuf.StackFrame parent = 2; +inline bool StackFrame_Data::has_parent() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void StackFrame_Data::set_has_parent() { + _has_bits_[0] |= 0x00000002u; +} +inline void StackFrame_Data::clear_has_parent() { + _has_bits_[0] &= ~0x00000002u; +} +inline void StackFrame_Data::clear_parent() { + if (parent_ != NULL) parent_->::mozilla::devtools::protobuf::StackFrame::Clear(); + clear_has_parent(); +} +inline const ::mozilla::devtools::protobuf::StackFrame& StackFrame_Data::parent() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.parent) + return parent_ != NULL ? *parent_ : *default_instance_->parent_; +} +inline ::mozilla::devtools::protobuf::StackFrame* StackFrame_Data::mutable_parent() { + set_has_parent(); + if (parent_ == NULL) parent_ = new ::mozilla::devtools::protobuf::StackFrame; + // @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.StackFrame.Data.parent) + return parent_; +} +inline ::mozilla::devtools::protobuf::StackFrame* StackFrame_Data::release_parent() { + clear_has_parent(); + ::mozilla::devtools::protobuf::StackFrame* temp = parent_; + parent_ = NULL; + return temp; +} +inline void StackFrame_Data::set_allocated_parent(::mozilla::devtools::protobuf::StackFrame* parent) { + delete parent_; + parent_ = parent; + if (parent) { + set_has_parent(); + } else { + clear_has_parent(); + } + // @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.StackFrame.Data.parent) +} + +// optional uint32 line = 3; +inline bool StackFrame_Data::has_line() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void StackFrame_Data::set_has_line() { + _has_bits_[0] |= 0x00000004u; +} +inline void StackFrame_Data::clear_has_line() { + _has_bits_[0] &= ~0x00000004u; +} +inline void StackFrame_Data::clear_line() { + line_ = 0u; + clear_has_line(); +} +inline ::google::protobuf::uint32 StackFrame_Data::line() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.line) + return line_; +} +inline void StackFrame_Data::set_line(::google::protobuf::uint32 value) { + set_has_line(); + line_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.StackFrame.Data.line) +} + +// optional uint32 column = 4; +inline bool StackFrame_Data::has_column() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void StackFrame_Data::set_has_column() { + _has_bits_[0] |= 0x00000008u; +} +inline void StackFrame_Data::clear_has_column() { + _has_bits_[0] &= ~0x00000008u; +} +inline void StackFrame_Data::clear_column() { + column_ = 0u; + clear_has_column(); +} +inline ::google::protobuf::uint32 StackFrame_Data::column() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.column) + return column_; +} +inline void StackFrame_Data::set_column(::google::protobuf::uint32 value) { + set_has_column(); + column_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.StackFrame.Data.column) +} + +// optional bytes source = 5; +inline bool StackFrame_Data::has_source() const { + return SourceOrRef_case() == kSource; +} +inline void StackFrame_Data::set_has_source() { + _oneof_case_[0] = kSource; +} +inline void StackFrame_Data::clear_source() { + if (has_source()) { + delete SourceOrRef_.source_; + clear_has_SourceOrRef(); + } +} +inline const ::std::string& StackFrame_Data::source() const { + if (has_source()) { + return *SourceOrRef_.source_; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void StackFrame_Data::set_source(const ::std::string& value) { + if (!has_source()) { + clear_SourceOrRef(); + set_has_source(); + SourceOrRef_.source_ = new ::std::string; + } + SourceOrRef_.source_->assign(value); +} +inline void StackFrame_Data::set_source(const char* value) { + if (!has_source()) { + clear_SourceOrRef(); + set_has_source(); + SourceOrRef_.source_ = new ::std::string; + } + SourceOrRef_.source_->assign(value); +} +inline void StackFrame_Data::set_source(const void* value, size_t size) { + if (!has_source()) { + clear_SourceOrRef(); + set_has_source(); + SourceOrRef_.source_ = new ::std::string; + } + SourceOrRef_.source_->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* StackFrame_Data::mutable_source() { + if (!has_source()) { + clear_SourceOrRef(); + set_has_source(); + SourceOrRef_.source_ = new ::std::string; + } + return SourceOrRef_.source_; +} +inline ::std::string* StackFrame_Data::release_source() { + if (has_source()) { + clear_has_SourceOrRef(); + ::std::string* temp = SourceOrRef_.source_; + SourceOrRef_.source_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void StackFrame_Data::set_allocated_source(::std::string* source) { + clear_SourceOrRef(); + if (source) { + set_has_source(); + SourceOrRef_.source_ = source; + } +} + +// optional uint64 sourceRef = 6; +inline bool StackFrame_Data::has_sourceref() const { + return SourceOrRef_case() == kSourceRef; +} +inline void StackFrame_Data::set_has_sourceref() { + _oneof_case_[0] = kSourceRef; +} +inline void StackFrame_Data::clear_sourceref() { + if (has_sourceref()) { + SourceOrRef_.sourceref_ = GOOGLE_ULONGLONG(0); + clear_has_SourceOrRef(); + } +} +inline ::google::protobuf::uint64 StackFrame_Data::sourceref() const { + if (has_sourceref()) { + return SourceOrRef_.sourceref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void StackFrame_Data::set_sourceref(::google::protobuf::uint64 value) { + if (!has_sourceref()) { + clear_SourceOrRef(); + set_has_sourceref(); + } + SourceOrRef_.sourceref_ = value; +} + +// optional bytes functionDisplayName = 7; +inline bool StackFrame_Data::has_functiondisplayname() const { + return FunctionDisplayNameOrRef_case() == kFunctionDisplayName; +} +inline void StackFrame_Data::set_has_functiondisplayname() { + _oneof_case_[1] = kFunctionDisplayName; +} +inline void StackFrame_Data::clear_functiondisplayname() { + if (has_functiondisplayname()) { + delete FunctionDisplayNameOrRef_.functiondisplayname_; + clear_has_FunctionDisplayNameOrRef(); + } +} +inline const ::std::string& StackFrame_Data::functiondisplayname() const { + if (has_functiondisplayname()) { + return *FunctionDisplayNameOrRef_.functiondisplayname_; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void StackFrame_Data::set_functiondisplayname(const ::std::string& value) { + if (!has_functiondisplayname()) { + clear_FunctionDisplayNameOrRef(); + set_has_functiondisplayname(); + FunctionDisplayNameOrRef_.functiondisplayname_ = new ::std::string; + } + FunctionDisplayNameOrRef_.functiondisplayname_->assign(value); +} +inline void StackFrame_Data::set_functiondisplayname(const char* value) { + if (!has_functiondisplayname()) { + clear_FunctionDisplayNameOrRef(); + set_has_functiondisplayname(); + FunctionDisplayNameOrRef_.functiondisplayname_ = new ::std::string; + } + FunctionDisplayNameOrRef_.functiondisplayname_->assign(value); +} +inline void StackFrame_Data::set_functiondisplayname(const void* value, size_t size) { + if (!has_functiondisplayname()) { + clear_FunctionDisplayNameOrRef(); + set_has_functiondisplayname(); + FunctionDisplayNameOrRef_.functiondisplayname_ = new ::std::string; + } + FunctionDisplayNameOrRef_.functiondisplayname_->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* StackFrame_Data::mutable_functiondisplayname() { + if (!has_functiondisplayname()) { + clear_FunctionDisplayNameOrRef(); + set_has_functiondisplayname(); + FunctionDisplayNameOrRef_.functiondisplayname_ = new ::std::string; + } + return FunctionDisplayNameOrRef_.functiondisplayname_; +} +inline ::std::string* StackFrame_Data::release_functiondisplayname() { + if (has_functiondisplayname()) { + clear_has_FunctionDisplayNameOrRef(); + ::std::string* temp = FunctionDisplayNameOrRef_.functiondisplayname_; + FunctionDisplayNameOrRef_.functiondisplayname_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void StackFrame_Data::set_allocated_functiondisplayname(::std::string* functiondisplayname) { + clear_FunctionDisplayNameOrRef(); + if (functiondisplayname) { + set_has_functiondisplayname(); + FunctionDisplayNameOrRef_.functiondisplayname_ = functiondisplayname; + } +} + +// optional uint64 functionDisplayNameRef = 8; +inline bool StackFrame_Data::has_functiondisplaynameref() const { + return FunctionDisplayNameOrRef_case() == kFunctionDisplayNameRef; +} +inline void StackFrame_Data::set_has_functiondisplaynameref() { + _oneof_case_[1] = kFunctionDisplayNameRef; +} +inline void StackFrame_Data::clear_functiondisplaynameref() { + if (has_functiondisplaynameref()) { + FunctionDisplayNameOrRef_.functiondisplaynameref_ = GOOGLE_ULONGLONG(0); + clear_has_FunctionDisplayNameOrRef(); + } +} +inline ::google::protobuf::uint64 StackFrame_Data::functiondisplaynameref() const { + if (has_functiondisplaynameref()) { + return FunctionDisplayNameOrRef_.functiondisplaynameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void StackFrame_Data::set_functiondisplaynameref(::google::protobuf::uint64 value) { + if (!has_functiondisplaynameref()) { + clear_FunctionDisplayNameOrRef(); + set_has_functiondisplaynameref(); + } + FunctionDisplayNameOrRef_.functiondisplaynameref_ = value; +} + +// optional bool isSystem = 9; +inline bool StackFrame_Data::has_issystem() const { + return (_has_bits_[0] & 0x00000100u) != 0; +} +inline void StackFrame_Data::set_has_issystem() { + _has_bits_[0] |= 0x00000100u; +} +inline void StackFrame_Data::clear_has_issystem() { + _has_bits_[0] &= ~0x00000100u; +} +inline void StackFrame_Data::clear_issystem() { + issystem_ = false; + clear_has_issystem(); +} +inline bool StackFrame_Data::issystem() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.isSystem) + return issystem_; +} +inline void StackFrame_Data::set_issystem(bool value) { + set_has_issystem(); + issystem_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.StackFrame.Data.isSystem) +} + +// optional bool isSelfHosted = 10; +inline bool StackFrame_Data::has_isselfhosted() const { + return (_has_bits_[0] & 0x00000200u) != 0; +} +inline void StackFrame_Data::set_has_isselfhosted() { + _has_bits_[0] |= 0x00000200u; +} +inline void StackFrame_Data::clear_has_isselfhosted() { + _has_bits_[0] &= ~0x00000200u; +} +inline void StackFrame_Data::clear_isselfhosted() { + isselfhosted_ = false; + clear_has_isselfhosted(); +} +inline bool StackFrame_Data::isselfhosted() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.StackFrame.Data.isSelfHosted) + return isselfhosted_; +} +inline void StackFrame_Data::set_isselfhosted(bool value) { + set_has_isselfhosted(); + isselfhosted_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.StackFrame.Data.isSelfHosted) +} + +inline bool StackFrame_Data::has_SourceOrRef() { + return SourceOrRef_case() != SOURCEORREF_NOT_SET; +} +inline void StackFrame_Data::clear_has_SourceOrRef() { + _oneof_case_[0] = SOURCEORREF_NOT_SET; +} +inline bool StackFrame_Data::has_FunctionDisplayNameOrRef() { + return FunctionDisplayNameOrRef_case() != FUNCTIONDISPLAYNAMEORREF_NOT_SET; +} +inline void StackFrame_Data::clear_has_FunctionDisplayNameOrRef() { + _oneof_case_[1] = FUNCTIONDISPLAYNAMEORREF_NOT_SET; +} +inline StackFrame_Data::SourceOrRefCase StackFrame_Data::SourceOrRef_case() const { + return StackFrame_Data::SourceOrRefCase(_oneof_case_[0]); +} +inline StackFrame_Data::FunctionDisplayNameOrRefCase StackFrame_Data::FunctionDisplayNameOrRef_case() const { + return StackFrame_Data::FunctionDisplayNameOrRefCase(_oneof_case_[1]); +} +// ------------------------------------------------------------------- + +// StackFrame + +// optional .mozilla.devtools.protobuf.StackFrame.Data data = 1; +inline bool StackFrame::has_data() const { + return StackFrameType_case() == kData; +} +inline void StackFrame::set_has_data() { + _oneof_case_[0] = kData; +} +inline void StackFrame::clear_data() { + if (has_data()) { + delete StackFrameType_.data_; + clear_has_StackFrameType(); + } +} +inline const ::mozilla::devtools::protobuf::StackFrame_Data& StackFrame::data() const { + return has_data() ? *StackFrameType_.data_ + : ::mozilla::devtools::protobuf::StackFrame_Data::default_instance(); +} +inline ::mozilla::devtools::protobuf::StackFrame_Data* StackFrame::mutable_data() { + if (!has_data()) { + clear_StackFrameType(); + set_has_data(); + StackFrameType_.data_ = new ::mozilla::devtools::protobuf::StackFrame_Data; + } + return StackFrameType_.data_; +} +inline ::mozilla::devtools::protobuf::StackFrame_Data* StackFrame::release_data() { + if (has_data()) { + clear_has_StackFrameType(); + ::mozilla::devtools::protobuf::StackFrame_Data* temp = StackFrameType_.data_; + StackFrameType_.data_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void StackFrame::set_allocated_data(::mozilla::devtools::protobuf::StackFrame_Data* data) { + clear_StackFrameType(); + if (data) { + set_has_data(); + StackFrameType_.data_ = data; + } +} + +// optional uint64 ref = 2; +inline bool StackFrame::has_ref() const { + return StackFrameType_case() == kRef; +} +inline void StackFrame::set_has_ref() { + _oneof_case_[0] = kRef; +} +inline void StackFrame::clear_ref() { + if (has_ref()) { + StackFrameType_.ref_ = GOOGLE_ULONGLONG(0); + clear_has_StackFrameType(); + } +} +inline ::google::protobuf::uint64 StackFrame::ref() const { + if (has_ref()) { + return StackFrameType_.ref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void StackFrame::set_ref(::google::protobuf::uint64 value) { + if (!has_ref()) { + clear_StackFrameType(); + set_has_ref(); + } + StackFrameType_.ref_ = value; +} + +inline bool StackFrame::has_StackFrameType() { + return StackFrameType_case() != STACKFRAMETYPE_NOT_SET; +} +inline void StackFrame::clear_has_StackFrameType() { + _oneof_case_[0] = STACKFRAMETYPE_NOT_SET; +} +inline StackFrame::StackFrameTypeCase StackFrame::StackFrameType_case() const { + return StackFrame::StackFrameTypeCase(_oneof_case_[0]); +} +// ------------------------------------------------------------------- + +// Node + +// optional uint64 id = 1; +inline bool Node::has_id() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Node::set_has_id() { + _has_bits_[0] |= 0x00000001u; +} +inline void Node::clear_has_id() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Node::clear_id() { + id_ = GOOGLE_ULONGLONG(0); + clear_has_id(); +} +inline ::google::protobuf::uint64 Node::id() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.id) + return id_; +} +inline void Node::set_id(::google::protobuf::uint64 value) { + set_has_id(); + id_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.id) +} + +// optional bytes typeName = 2; +inline bool Node::has_typename_() const { + return TypeNameOrRef_case() == kTypeName; +} +inline void Node::set_has_typename_() { + _oneof_case_[0] = kTypeName; +} +inline void Node::clear_typename_() { + if (has_typename_()) { + delete TypeNameOrRef_.typename__; + clear_has_TypeNameOrRef(); + } +} +inline const ::std::string& Node::typename_() const { + if (has_typename_()) { + return *TypeNameOrRef_.typename__; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void Node::set_typename_(const ::std::string& value) { + if (!has_typename_()) { + clear_TypeNameOrRef(); + set_has_typename_(); + TypeNameOrRef_.typename__ = new ::std::string; + } + TypeNameOrRef_.typename__->assign(value); +} +inline void Node::set_typename_(const char* value) { + if (!has_typename_()) { + clear_TypeNameOrRef(); + set_has_typename_(); + TypeNameOrRef_.typename__ = new ::std::string; + } + TypeNameOrRef_.typename__->assign(value); +} +inline void Node::set_typename_(const void* value, size_t size) { + if (!has_typename_()) { + clear_TypeNameOrRef(); + set_has_typename_(); + TypeNameOrRef_.typename__ = new ::std::string; + } + TypeNameOrRef_.typename__->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* Node::mutable_typename_() { + if (!has_typename_()) { + clear_TypeNameOrRef(); + set_has_typename_(); + TypeNameOrRef_.typename__ = new ::std::string; + } + return TypeNameOrRef_.typename__; +} +inline ::std::string* Node::release_typename_() { + if (has_typename_()) { + clear_has_TypeNameOrRef(); + ::std::string* temp = TypeNameOrRef_.typename__; + TypeNameOrRef_.typename__ = NULL; + return temp; + } else { + return NULL; + } +} +inline void Node::set_allocated_typename_(::std::string* typename_) { + clear_TypeNameOrRef(); + if (typename_) { + set_has_typename_(); + TypeNameOrRef_.typename__ = typename_; + } +} + +// optional uint64 typeNameRef = 3; +inline bool Node::has_typenameref() const { + return TypeNameOrRef_case() == kTypeNameRef; +} +inline void Node::set_has_typenameref() { + _oneof_case_[0] = kTypeNameRef; +} +inline void Node::clear_typenameref() { + if (has_typenameref()) { + TypeNameOrRef_.typenameref_ = GOOGLE_ULONGLONG(0); + clear_has_TypeNameOrRef(); + } +} +inline ::google::protobuf::uint64 Node::typenameref() const { + if (has_typenameref()) { + return TypeNameOrRef_.typenameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void Node::set_typenameref(::google::protobuf::uint64 value) { + if (!has_typenameref()) { + clear_TypeNameOrRef(); + set_has_typenameref(); + } + TypeNameOrRef_.typenameref_ = value; +} + +// optional uint64 size = 4; +inline bool Node::has_size() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void Node::set_has_size() { + _has_bits_[0] |= 0x00000008u; +} +inline void Node::clear_has_size() { + _has_bits_[0] &= ~0x00000008u; +} +inline void Node::clear_size() { + size_ = GOOGLE_ULONGLONG(0); + clear_has_size(); +} +inline ::google::protobuf::uint64 Node::size() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.size) + return size_; +} +inline void Node::set_size(::google::protobuf::uint64 value) { + set_has_size(); + size_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.size) +} + +// repeated .mozilla.devtools.protobuf.Edge edges = 5; +inline int Node::edges_size() const { + return edges_.size(); +} +inline void Node::clear_edges() { + edges_.Clear(); +} +inline const ::mozilla::devtools::protobuf::Edge& Node::edges(int index) const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.edges) + return edges_.Get(index); +} +inline ::mozilla::devtools::protobuf::Edge* Node::mutable_edges(int index) { + // @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.edges) + return edges_.Mutable(index); +} +inline ::mozilla::devtools::protobuf::Edge* Node::add_edges() { + // @@protoc_insertion_point(field_add:mozilla.devtools.protobuf.Node.edges) + return edges_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >& +Node::edges() const { + // @@protoc_insertion_point(field_list:mozilla.devtools.protobuf.Node.edges) + return edges_; +} +inline ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >* +Node::mutable_edges() { + // @@protoc_insertion_point(field_mutable_list:mozilla.devtools.protobuf.Node.edges) + return &edges_; +} + +// optional .mozilla.devtools.protobuf.StackFrame allocationStack = 6; +inline bool Node::has_allocationstack() const { + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void Node::set_has_allocationstack() { + _has_bits_[0] |= 0x00000020u; +} +inline void Node::clear_has_allocationstack() { + _has_bits_[0] &= ~0x00000020u; +} +inline void Node::clear_allocationstack() { + if (allocationstack_ != NULL) allocationstack_->::mozilla::devtools::protobuf::StackFrame::Clear(); + clear_has_allocationstack(); +} +inline const ::mozilla::devtools::protobuf::StackFrame& Node::allocationstack() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.allocationStack) + return allocationstack_ != NULL ? *allocationstack_ : *default_instance_->allocationstack_; +} +inline ::mozilla::devtools::protobuf::StackFrame* Node::mutable_allocationstack() { + set_has_allocationstack(); + if (allocationstack_ == NULL) allocationstack_ = new ::mozilla::devtools::protobuf::StackFrame; + // @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.allocationStack) + return allocationstack_; +} +inline ::mozilla::devtools::protobuf::StackFrame* Node::release_allocationstack() { + clear_has_allocationstack(); + ::mozilla::devtools::protobuf::StackFrame* temp = allocationstack_; + allocationstack_ = NULL; + return temp; +} +inline void Node::set_allocated_allocationstack(::mozilla::devtools::protobuf::StackFrame* allocationstack) { + delete allocationstack_; + allocationstack_ = allocationstack; + if (allocationstack) { + set_has_allocationstack(); + } else { + clear_has_allocationstack(); + } + // @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.allocationStack) +} + +// optional bytes jsObjectClassName = 7; +inline bool Node::has_jsobjectclassname() const { + return JSObjectClassNameOrRef_case() == kJsObjectClassName; +} +inline void Node::set_has_jsobjectclassname() { + _oneof_case_[1] = kJsObjectClassName; +} +inline void Node::clear_jsobjectclassname() { + if (has_jsobjectclassname()) { + delete JSObjectClassNameOrRef_.jsobjectclassname_; + clear_has_JSObjectClassNameOrRef(); + } +} +inline const ::std::string& Node::jsobjectclassname() const { + if (has_jsobjectclassname()) { + return *JSObjectClassNameOrRef_.jsobjectclassname_; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void Node::set_jsobjectclassname(const ::std::string& value) { + if (!has_jsobjectclassname()) { + clear_JSObjectClassNameOrRef(); + set_has_jsobjectclassname(); + JSObjectClassNameOrRef_.jsobjectclassname_ = new ::std::string; + } + JSObjectClassNameOrRef_.jsobjectclassname_->assign(value); +} +inline void Node::set_jsobjectclassname(const char* value) { + if (!has_jsobjectclassname()) { + clear_JSObjectClassNameOrRef(); + set_has_jsobjectclassname(); + JSObjectClassNameOrRef_.jsobjectclassname_ = new ::std::string; + } + JSObjectClassNameOrRef_.jsobjectclassname_->assign(value); +} +inline void Node::set_jsobjectclassname(const void* value, size_t size) { + if (!has_jsobjectclassname()) { + clear_JSObjectClassNameOrRef(); + set_has_jsobjectclassname(); + JSObjectClassNameOrRef_.jsobjectclassname_ = new ::std::string; + } + JSObjectClassNameOrRef_.jsobjectclassname_->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* Node::mutable_jsobjectclassname() { + if (!has_jsobjectclassname()) { + clear_JSObjectClassNameOrRef(); + set_has_jsobjectclassname(); + JSObjectClassNameOrRef_.jsobjectclassname_ = new ::std::string; + } + return JSObjectClassNameOrRef_.jsobjectclassname_; +} +inline ::std::string* Node::release_jsobjectclassname() { + if (has_jsobjectclassname()) { + clear_has_JSObjectClassNameOrRef(); + ::std::string* temp = JSObjectClassNameOrRef_.jsobjectclassname_; + JSObjectClassNameOrRef_.jsobjectclassname_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void Node::set_allocated_jsobjectclassname(::std::string* jsobjectclassname) { + clear_JSObjectClassNameOrRef(); + if (jsobjectclassname) { + set_has_jsobjectclassname(); + JSObjectClassNameOrRef_.jsobjectclassname_ = jsobjectclassname; + } +} + +// optional uint64 jsObjectClassNameRef = 8; +inline bool Node::has_jsobjectclassnameref() const { + return JSObjectClassNameOrRef_case() == kJsObjectClassNameRef; +} +inline void Node::set_has_jsobjectclassnameref() { + _oneof_case_[1] = kJsObjectClassNameRef; +} +inline void Node::clear_jsobjectclassnameref() { + if (has_jsobjectclassnameref()) { + JSObjectClassNameOrRef_.jsobjectclassnameref_ = GOOGLE_ULONGLONG(0); + clear_has_JSObjectClassNameOrRef(); + } +} +inline ::google::protobuf::uint64 Node::jsobjectclassnameref() const { + if (has_jsobjectclassnameref()) { + return JSObjectClassNameOrRef_.jsobjectclassnameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void Node::set_jsobjectclassnameref(::google::protobuf::uint64 value) { + if (!has_jsobjectclassnameref()) { + clear_JSObjectClassNameOrRef(); + set_has_jsobjectclassnameref(); + } + JSObjectClassNameOrRef_.jsobjectclassnameref_ = value; +} + +// optional uint32 coarseType = 9 [default = 0]; +inline bool Node::has_coarsetype() const { + return (_has_bits_[0] & 0x00000100u) != 0; +} +inline void Node::set_has_coarsetype() { + _has_bits_[0] |= 0x00000100u; +} +inline void Node::clear_has_coarsetype() { + _has_bits_[0] &= ~0x00000100u; +} +inline void Node::clear_coarsetype() { + coarsetype_ = 0u; + clear_has_coarsetype(); +} +inline ::google::protobuf::uint32 Node::coarsetype() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.coarseType) + return coarsetype_; +} +inline void Node::set_coarsetype(::google::protobuf::uint32 value) { + set_has_coarsetype(); + coarsetype_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.coarseType) +} + +// optional bytes scriptFilename = 10; +inline bool Node::has_scriptfilename() const { + return ScriptFilenameOrRef_case() == kScriptFilename; +} +inline void Node::set_has_scriptfilename() { + _oneof_case_[2] = kScriptFilename; +} +inline void Node::clear_scriptfilename() { + if (has_scriptfilename()) { + delete ScriptFilenameOrRef_.scriptfilename_; + clear_has_ScriptFilenameOrRef(); + } +} +inline const ::std::string& Node::scriptfilename() const { + if (has_scriptfilename()) { + return *ScriptFilenameOrRef_.scriptfilename_; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void Node::set_scriptfilename(const ::std::string& value) { + if (!has_scriptfilename()) { + clear_ScriptFilenameOrRef(); + set_has_scriptfilename(); + ScriptFilenameOrRef_.scriptfilename_ = new ::std::string; + } + ScriptFilenameOrRef_.scriptfilename_->assign(value); +} +inline void Node::set_scriptfilename(const char* value) { + if (!has_scriptfilename()) { + clear_ScriptFilenameOrRef(); + set_has_scriptfilename(); + ScriptFilenameOrRef_.scriptfilename_ = new ::std::string; + } + ScriptFilenameOrRef_.scriptfilename_->assign(value); +} +inline void Node::set_scriptfilename(const void* value, size_t size) { + if (!has_scriptfilename()) { + clear_ScriptFilenameOrRef(); + set_has_scriptfilename(); + ScriptFilenameOrRef_.scriptfilename_ = new ::std::string; + } + ScriptFilenameOrRef_.scriptfilename_->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* Node::mutable_scriptfilename() { + if (!has_scriptfilename()) { + clear_ScriptFilenameOrRef(); + set_has_scriptfilename(); + ScriptFilenameOrRef_.scriptfilename_ = new ::std::string; + } + return ScriptFilenameOrRef_.scriptfilename_; +} +inline ::std::string* Node::release_scriptfilename() { + if (has_scriptfilename()) { + clear_has_ScriptFilenameOrRef(); + ::std::string* temp = ScriptFilenameOrRef_.scriptfilename_; + ScriptFilenameOrRef_.scriptfilename_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void Node::set_allocated_scriptfilename(::std::string* scriptfilename) { + clear_ScriptFilenameOrRef(); + if (scriptfilename) { + set_has_scriptfilename(); + ScriptFilenameOrRef_.scriptfilename_ = scriptfilename; + } +} + +// optional uint64 scriptFilenameRef = 11; +inline bool Node::has_scriptfilenameref() const { + return ScriptFilenameOrRef_case() == kScriptFilenameRef; +} +inline void Node::set_has_scriptfilenameref() { + _oneof_case_[2] = kScriptFilenameRef; +} +inline void Node::clear_scriptfilenameref() { + if (has_scriptfilenameref()) { + ScriptFilenameOrRef_.scriptfilenameref_ = GOOGLE_ULONGLONG(0); + clear_has_ScriptFilenameOrRef(); + } +} +inline ::google::protobuf::uint64 Node::scriptfilenameref() const { + if (has_scriptfilenameref()) { + return ScriptFilenameOrRef_.scriptfilenameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void Node::set_scriptfilenameref(::google::protobuf::uint64 value) { + if (!has_scriptfilenameref()) { + clear_ScriptFilenameOrRef(); + set_has_scriptfilenameref(); + } + ScriptFilenameOrRef_.scriptfilenameref_ = value; +} + +inline bool Node::has_TypeNameOrRef() { + return TypeNameOrRef_case() != TYPENAMEORREF_NOT_SET; +} +inline void Node::clear_has_TypeNameOrRef() { + _oneof_case_[0] = TYPENAMEORREF_NOT_SET; +} +inline bool Node::has_JSObjectClassNameOrRef() { + return JSObjectClassNameOrRef_case() != JSOBJECTCLASSNAMEORREF_NOT_SET; +} +inline void Node::clear_has_JSObjectClassNameOrRef() { + _oneof_case_[1] = JSOBJECTCLASSNAMEORREF_NOT_SET; +} +inline bool Node::has_ScriptFilenameOrRef() { + return ScriptFilenameOrRef_case() != SCRIPTFILENAMEORREF_NOT_SET; +} +inline void Node::clear_has_ScriptFilenameOrRef() { + _oneof_case_[2] = SCRIPTFILENAMEORREF_NOT_SET; +} +inline Node::TypeNameOrRefCase Node::TypeNameOrRef_case() const { + return Node::TypeNameOrRefCase(_oneof_case_[0]); +} +inline Node::JSObjectClassNameOrRefCase Node::JSObjectClassNameOrRef_case() const { + return Node::JSObjectClassNameOrRefCase(_oneof_case_[1]); +} +inline Node::ScriptFilenameOrRefCase Node::ScriptFilenameOrRef_case() const { + return Node::ScriptFilenameOrRefCase(_oneof_case_[2]); +} +// ------------------------------------------------------------------- + +// Edge + +// optional uint64 referent = 1; +inline bool Edge::has_referent() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void Edge::set_has_referent() { + _has_bits_[0] |= 0x00000001u; +} +inline void Edge::clear_has_referent() { + _has_bits_[0] &= ~0x00000001u; +} +inline void Edge::clear_referent() { + referent_ = GOOGLE_ULONGLONG(0); + clear_has_referent(); +} +inline ::google::protobuf::uint64 Edge::referent() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Edge.referent) + return referent_; +} +inline void Edge::set_referent(::google::protobuf::uint64 value) { + set_has_referent(); + referent_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Edge.referent) +} + +// optional bytes name = 2; +inline bool Edge::has_name() const { + return EdgeNameOrRef_case() == kName; +} +inline void Edge::set_has_name() { + _oneof_case_[0] = kName; +} +inline void Edge::clear_name() { + if (has_name()) { + delete EdgeNameOrRef_.name_; + clear_has_EdgeNameOrRef(); + } +} +inline const ::std::string& Edge::name() const { + if (has_name()) { + return *EdgeNameOrRef_.name_; + } + return ::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void Edge::set_name(const ::std::string& value) { + if (!has_name()) { + clear_EdgeNameOrRef(); + set_has_name(); + EdgeNameOrRef_.name_ = new ::std::string; + } + EdgeNameOrRef_.name_->assign(value); +} +inline void Edge::set_name(const char* value) { + if (!has_name()) { + clear_EdgeNameOrRef(); + set_has_name(); + EdgeNameOrRef_.name_ = new ::std::string; + } + EdgeNameOrRef_.name_->assign(value); +} +inline void Edge::set_name(const void* value, size_t size) { + if (!has_name()) { + clear_EdgeNameOrRef(); + set_has_name(); + EdgeNameOrRef_.name_ = new ::std::string; + } + EdgeNameOrRef_.name_->assign( + reinterpret_cast<const char*>(value), size); +} +inline ::std::string* Edge::mutable_name() { + if (!has_name()) { + clear_EdgeNameOrRef(); + set_has_name(); + EdgeNameOrRef_.name_ = new ::std::string; + } + return EdgeNameOrRef_.name_; +} +inline ::std::string* Edge::release_name() { + if (has_name()) { + clear_has_EdgeNameOrRef(); + ::std::string* temp = EdgeNameOrRef_.name_; + EdgeNameOrRef_.name_ = NULL; + return temp; + } else { + return NULL; + } +} +inline void Edge::set_allocated_name(::std::string* name) { + clear_EdgeNameOrRef(); + if (name) { + set_has_name(); + EdgeNameOrRef_.name_ = name; + } +} + +// optional uint64 nameRef = 3; +inline bool Edge::has_nameref() const { + return EdgeNameOrRef_case() == kNameRef; +} +inline void Edge::set_has_nameref() { + _oneof_case_[0] = kNameRef; +} +inline void Edge::clear_nameref() { + if (has_nameref()) { + EdgeNameOrRef_.nameref_ = GOOGLE_ULONGLONG(0); + clear_has_EdgeNameOrRef(); + } +} +inline ::google::protobuf::uint64 Edge::nameref() const { + if (has_nameref()) { + return EdgeNameOrRef_.nameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void Edge::set_nameref(::google::protobuf::uint64 value) { + if (!has_nameref()) { + clear_EdgeNameOrRef(); + set_has_nameref(); + } + EdgeNameOrRef_.nameref_ = value; +} + +inline bool Edge::has_EdgeNameOrRef() { + return EdgeNameOrRef_case() != EDGENAMEORREF_NOT_SET; +} +inline void Edge::clear_has_EdgeNameOrRef() { + _oneof_case_[0] = EDGENAMEORREF_NOT_SET; +} +inline Edge::EdgeNameOrRefCase Edge::EdgeNameOrRef_case() const { + return Edge::EdgeNameOrRefCase(_oneof_case_[0]); +} + +// @@protoc_insertion_point(namespace_scope) + +} // namespace protobuf +} // namespace devtools +} // namespace mozilla + +#ifndef SWIG +namespace google { +namespace protobuf { + + +} // namespace google +} // namespace protobuf +#endif // SWIG + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_CoreDump_2eproto__INCLUDED diff --git a/dom/heapsnapshot/CoreDump.proto b/dom/heapsnapshot/CoreDump.proto new file mode 100644 index 000000000..24a223e11 --- /dev/null +++ b/dom/heapsnapshot/CoreDump.proto @@ -0,0 +1,143 @@ +/* -*- Mode: protobuf; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +// # Core Dumps +// +// A core dump is a serialized snapshot of the heap graph. We serialize the heap +// as a series of protobuf messages with each message prefixed by its Varint32 +// byte size so we can delimit individual protobuf messages (protobuf parsers +// cannot determine where a message ends on their own). +// +// The first protobuf message is an instance of the `Metadata` message. All +// subsequent messages will be instances of the `Node` message. The first of +// these `Node` messages is the root node of the serialized heap graph. Here is +// a diagram of our core dump format: +// +// +-----------------------------------------------------------------------+ +// | Varint32: The size of following `Metadata` message. | +// +-----------------------------------------------------------------------+ +// | message: The core dump `Metadata` message. | +// +-----------------------------------------------------------------------+ +// | Varint32: The size of the following `Node` message. | +// +-----------------------------------------------------------------------+ +// | message: The first `Node` message. This is the root node. | +// +-----------------------------------------------------------------------+ +// | Varint32: The size of the following `Node` message. | +// +-----------------------------------------------------------------------+ +// | message: A `Node` message. | +// +-----------------------------------------------------------------------+ +// | Varint32: The size of the following `Node` message. | +// +-----------------------------------------------------------------------+ +// | message: A `Node` message. | +// +-----------------------------------------------------------------------+ +// | . | +// | . | +// | . | +// +-----------------------------------------------------------------------+ +// +// Core dumps should always be written with a +// `google::protobuf::io::GzipOutputStream` and read from a +// `google::protobuf::io::GzipInputStream`. +// +// Note that all strings are de-duplicated. The first time the N^th unique +// string is encountered, the full string is serialized. Subsequent times that +// same string is encountered, it is referenced by N. This de-duplication +// happens across string properties, not on a per-property basis. For example, +// if the same K^th unique string is first used as an Edge::EdgeNameOrRef and +// then as a StackFrame::Data::FunctionDisplayNameOrRef, the first will be the +// actual string as the functionDisplayName oneof property, and the second will +// be a reference to the first as the edgeNameRef oneof property whose value is +// K. +// +// We would ordinarily abstract these de-duplicated strings with messages of +// their own, but unfortunately, the protobuf compiler does not have a way to +// inline a messsage within another message and the child message must be +// referenced by pointer. This leads to extra mallocs that we wish to avoid. + + +package mozilla.devtools.protobuf; + +// A collection of metadata about this core dump. +message Metadata { + // Number of microseconds since midnight (00:00:00) 1 January 1970 UTC. + optional uint64 timeStamp = 1; +} + +// A serialized version of `JS::ubi::StackFrame`. Older parent frame tails are +// de-duplicated to cut down on [de]serialization and size costs. +message StackFrame { + oneof StackFrameType { + // This is the first time this stack frame has been serialized, and so + // here is all of its data. + Data data = 1; + // A reference to a stack frame that has already been serialized and has + // the given number as its id. + uint64 ref = 2; + } + + message Data { + optional uint64 id = 1; + optional StackFrame parent = 2; + optional uint32 line = 3; + optional uint32 column = 4; + + // De-duplicated two-byte string. + oneof SourceOrRef { + bytes source = 5; + uint64 sourceRef = 6; + } + + // De-duplicated two-byte string. + oneof FunctionDisplayNameOrRef { + bytes functionDisplayName = 7; + uint64 functionDisplayNameRef = 8; + } + + optional bool isSystem = 9; + optional bool isSelfHosted = 10; + } +} + +// A serialized version of `JS::ubi::Node` and its outgoing edges. +message Node { + optional uint64 id = 1; + + // De-duplicated two-byte string. + oneof TypeNameOrRef { + bytes typeName = 2; + uint64 typeNameRef = 3; + } + + optional uint64 size = 4; + repeated Edge edges = 5; + optional StackFrame allocationStack = 6; + + // De-duplicated one-byte string. + oneof JSObjectClassNameOrRef { + bytes jsObjectClassName = 7; + uint64 jsObjectClassNameRef = 8; + } + + // JS::ubi::CoarseType. Defaults to Other. + optional uint32 coarseType = 9 [default = 0]; + + // De-duplicated one-byte string. + oneof ScriptFilenameOrRef { + bytes scriptFilename = 10; + uint64 scriptFilenameRef = 11; + } +} + +// A serialized edge from the heap graph. +message Edge { + optional uint64 referent = 1; + + // De-duplicated two-byte string. + oneof EdgeNameOrRef { + bytes name = 2; + uint64 nameRef = 3; + } +} diff --git a/dom/heapsnapshot/DeserializedNode.cpp b/dom/heapsnapshot/DeserializedNode.cpp new file mode 100644 index 000000000..fac4cccb9 --- /dev/null +++ b/dom/heapsnapshot/DeserializedNode.cpp @@ -0,0 +1,150 @@ +/* -*- 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 "mozilla/devtools/DeserializedNode.h" +#include "mozilla/devtools/HeapSnapshot.h" +#include "nsCRTGlue.h" + +namespace mozilla { +namespace devtools { + +DeserializedEdge::DeserializedEdge(DeserializedEdge&& rhs) +{ + referent = rhs.referent; + name = rhs.name; +} + +DeserializedEdge& DeserializedEdge::operator=(DeserializedEdge&& rhs) +{ + MOZ_ASSERT(&rhs != this); + this->~DeserializedEdge(); + new(this) DeserializedEdge(Move(rhs)); + return *this; +} + +JS::ubi::Node +DeserializedNode::getEdgeReferent(const DeserializedEdge& edge) +{ + auto ptr = owner->nodes.lookup(edge.referent); + MOZ_ASSERT(ptr); + + // `HashSets` only provide const access to their values, because mutating a + // value might change its hash, rendering it unfindable in the set. + // Unfortunately, the `ubi::Node` constructor requires a non-const pointer to + // its referent. However, the only aspect of a `DeserializedNode` we hash on + // is its id, which can't be changed via `ubi::Node`, so this cast can't cause + // the trouble `HashSet` is concerned a non-const reference would cause. + return JS::ubi::Node(const_cast<DeserializedNode*>(&*ptr)); +} + +JS::ubi::StackFrame +DeserializedStackFrame::getParentStackFrame() const +{ + MOZ_ASSERT(parent.isSome()); + auto ptr = owner->frames.lookup(parent.ref()); + MOZ_ASSERT(ptr); + // See above comment in DeserializedNode::getEdgeReferent about why this + // const_cast is needed and safe. + return JS::ubi::StackFrame(const_cast<DeserializedStackFrame*>(&*ptr)); +} + +} // namespace devtools +} // namespace mozilla + +namespace JS { +namespace ubi { + +using mozilla::devtools::DeserializedEdge; + +const char16_t Concrete<DeserializedNode>::concreteTypeName[] = + u"mozilla::devtools::DeserializedNode"; + +const char16_t* +Concrete<DeserializedNode>::typeName() const +{ + return get().typeName; +} + +Node::Size +Concrete<DeserializedNode>::size(mozilla::MallocSizeOf mallocSizeof) const +{ + return get().size; +} + +class DeserializedEdgeRange : public EdgeRange +{ + DeserializedNode* node; + Edge currentEdge; + size_t i; + + void settle() { + if (i >= node->edges.length()) { + front_ = nullptr; + return; + } + + auto& edge = node->edges[i]; + auto referent = node->getEdgeReferent(edge); + currentEdge = mozilla::Move(Edge(edge.name ? NS_strdup(edge.name) : nullptr, + referent)); + front_ = ¤tEdge; + } + +public: + explicit DeserializedEdgeRange(DeserializedNode& node) + : node(&node) + , i(0) + { + settle(); + } + + void popFront() override + { + i++; + settle(); + } +}; + +StackFrame +Concrete<DeserializedNode>::allocationStack() const +{ + MOZ_ASSERT(hasAllocationStack()); + auto id = get().allocationStack.ref(); + auto ptr = get().owner->frames.lookup(id); + MOZ_ASSERT(ptr); + // See above comment in DeserializedNode::getEdgeReferent about why this + // const_cast is needed and safe. + return JS::ubi::StackFrame(const_cast<DeserializedStackFrame*>(&*ptr)); +} + + +js::UniquePtr<EdgeRange> +Concrete<DeserializedNode>::edges(JSContext* cx, bool) const +{ + js::UniquePtr<DeserializedEdgeRange> range(js_new<DeserializedEdgeRange>(get())); + + if (!range) + return nullptr; + + return js::UniquePtr<EdgeRange>(range.release()); +} + +StackFrame +ConcreteStackFrame<DeserializedStackFrame>::parent() const +{ + return get().parent.isNothing() ? StackFrame() : get().getParentStackFrame(); +} + +bool +ConcreteStackFrame<DeserializedStackFrame>::constructSavedFrameStack( + JSContext* cx, + MutableHandleObject outSavedFrameStack) const +{ + StackFrame f(&get()); + return ConstructSavedFrameStackSlow(cx, f, outSavedFrameStack); +} + +} // namespace ubi +} // namespace JS diff --git a/dom/heapsnapshot/DeserializedNode.h b/dom/heapsnapshot/DeserializedNode.h new file mode 100644 index 000000000..60d1fb408 --- /dev/null +++ b/dom/heapsnapshot/DeserializedNode.h @@ -0,0 +1,317 @@ +/* -*- 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_devtools_DeserializedNode__ +#define mozilla_devtools_DeserializedNode__ + +#include "js/UbiNode.h" +#include "js/UniquePtr.h" +#include "mozilla/devtools/CoreDump.pb.h" +#include "mozilla/Maybe.h" +#include "mozilla/Move.h" +#include "mozilla/Vector.h" + +// `Deserialized{Node,Edge}` translate protobuf messages from our core dump +// format into structures we can rely upon for implementing `JS::ubi::Node` +// specializations on top of. All of the properties of the protobuf messages are +// optional for future compatibility, and this is the layer where we validate +// that the properties that do actually exist in any given message fulfill our +// semantic requirements. +// +// Both `DeserializedNode` and `DeserializedEdge` are always owned by a +// `HeapSnapshot` instance, and their lifetimes must not extend after that of +// their owning `HeapSnapshot`. + +namespace mozilla { +namespace devtools { + +class HeapSnapshot; + +using NodeId = uint64_t; +using StackFrameId = uint64_t; + +// A `DeserializedEdge` represents an edge in the heap graph pointing to the +// node with id equal to `DeserializedEdge::referent` that we deserialized from +// a core dump. +struct DeserializedEdge { + NodeId referent; + // A borrowed reference to a string owned by this node's owning HeapSnapshot. + const char16_t* name; + + explicit DeserializedEdge(NodeId referent, const char16_t* edgeName = nullptr) + : referent(referent) + , name(edgeName) + { } + DeserializedEdge(DeserializedEdge&& rhs); + DeserializedEdge& operator=(DeserializedEdge&& rhs); + +private: + DeserializedEdge(const DeserializedEdge&) = delete; + DeserializedEdge& operator=(const DeserializedEdge&) = delete; +}; + +// A `DeserializedNode` is a node in the heap graph that we deserialized from a +// core dump. +struct DeserializedNode { + using EdgeVector = Vector<DeserializedEdge>; + using UniqueStringPtr = UniquePtr<char16_t[]>; + + NodeId id; + JS::ubi::CoarseType coarseType; + // A borrowed reference to a string owned by this node's owning HeapSnapshot. + const char16_t* typeName; + uint64_t size; + EdgeVector edges; + Maybe<StackFrameId> allocationStack; + // A borrowed reference to a string owned by this node's owning HeapSnapshot. + const char* jsObjectClassName; + // A borrowed reference to a string owned by this node's owning HeapSnapshot. + const char* scriptFilename; + // A weak pointer to this node's owning `HeapSnapshot`. Safe without + // AddRef'ing because this node's lifetime is equal to that of its owner. + HeapSnapshot* owner; + + DeserializedNode(NodeId id, + JS::ubi::CoarseType coarseType, + const char16_t* typeName, + uint64_t size, + EdgeVector&& edges, + Maybe<StackFrameId> allocationStack, + const char* className, + const char* filename, + HeapSnapshot& owner) + : id(id) + , coarseType(coarseType) + , typeName(typeName) + , size(size) + , edges(Move(edges)) + , allocationStack(allocationStack) + , jsObjectClassName(className) + , scriptFilename(filename) + , owner(&owner) + { } + virtual ~DeserializedNode() { } + + DeserializedNode(DeserializedNode&& rhs) + : id(rhs.id) + , coarseType(rhs.coarseType) + , typeName(rhs.typeName) + , size(rhs.size) + , edges(Move(rhs.edges)) + , allocationStack(rhs.allocationStack) + , jsObjectClassName(rhs.jsObjectClassName) + , scriptFilename(rhs.scriptFilename) + , owner(rhs.owner) + { } + + DeserializedNode& operator=(DeserializedNode&& rhs) + { + MOZ_ASSERT(&rhs != this); + this->~DeserializedNode(); + new(this) DeserializedNode(Move(rhs)); + return *this; + } + + // Get a borrowed reference to the given edge's referent. This method is + // virtual to provide a hook for gmock and gtest. + virtual JS::ubi::Node getEdgeReferent(const DeserializedEdge& edge); + + struct HashPolicy; + +protected: + // This is only for use with `MockDeserializedNode` in testing. + DeserializedNode(NodeId id, const char16_t* typeName, uint64_t size) + : id(id) + , coarseType(JS::ubi::CoarseType::Other) + , typeName(typeName) + , size(size) + , edges() + , allocationStack(Nothing()) + , jsObjectClassName(nullptr) + , scriptFilename(nullptr) + , owner(nullptr) + { } + +private: + DeserializedNode(const DeserializedNode&) = delete; + DeserializedNode& operator=(const DeserializedNode&) = delete; +}; + +static inline js::HashNumber +hashIdDerivedFromPtr(uint64_t id) +{ + // NodeIds and StackFrameIds are always 64 bits, but they are derived from + // the original referents' addresses, which could have been either 32 or 64 + // bits long. As such, NodeId and StackFrameId have little entropy in their + // bottom three bits, and may or may not have entropy in their upper 32 + // bits. This hash should manage both cases well. + id >>= 3; + return js::HashNumber((id >> 32) ^ id); +} + +struct DeserializedNode::HashPolicy +{ + using Lookup = NodeId; + + static js::HashNumber hash(const Lookup& lookup) { + return hashIdDerivedFromPtr(lookup); + } + + static bool match(const DeserializedNode& existing, const Lookup& lookup) { + return existing.id == lookup; + } +}; + +// A `DeserializedStackFrame` is a stack frame referred to by a thing in the +// heap graph that we deserialized from a core dump. +struct DeserializedStackFrame { + StackFrameId id; + Maybe<StackFrameId> parent; + uint32_t line; + uint32_t column; + // Borrowed references to strings owned by this DeserializedStackFrame's + // owning HeapSnapshot. + const char16_t* source; + const char16_t* functionDisplayName; + bool isSystem; + bool isSelfHosted; + // A weak pointer to this frame's owning `HeapSnapshot`. Safe without + // AddRef'ing because this frame's lifetime is equal to that of its owner. + HeapSnapshot* owner; + + explicit DeserializedStackFrame(StackFrameId id, + const Maybe<StackFrameId>& parent, + uint32_t line, + uint32_t column, + const char16_t* source, + const char16_t* functionDisplayName, + bool isSystem, + bool isSelfHosted, + HeapSnapshot& owner) + : id(id) + , parent(parent) + , line(line) + , column(column) + , source(source) + , functionDisplayName(functionDisplayName) + , isSystem(isSystem) + , isSelfHosted(isSelfHosted) + , owner(&owner) + { + MOZ_ASSERT(source); + } + + JS::ubi::StackFrame getParentStackFrame() const; + + struct HashPolicy; + +protected: + // This is exposed only for MockDeserializedStackFrame in the gtests. + explicit DeserializedStackFrame() + : id(0) + , parent(Nothing()) + , line(0) + , column(0) + , source(nullptr) + , functionDisplayName(nullptr) + , isSystem(false) + , isSelfHosted(false) + , owner(nullptr) + { }; +}; + +struct DeserializedStackFrame::HashPolicy { + using Lookup = StackFrameId; + + static js::HashNumber hash(const Lookup& lookup) { + return hashIdDerivedFromPtr(lookup); + } + + static bool match(const DeserializedStackFrame& existing, const Lookup& lookup) { + return existing.id == lookup; + } +}; + +} // namespace devtools +} // namespace mozilla + +namespace JS { +namespace ubi { + +using mozilla::devtools::DeserializedNode; +using mozilla::devtools::DeserializedStackFrame; + +template<> +class Concrete<DeserializedNode> : public Base +{ +protected: + explicit Concrete(DeserializedNode* ptr) : Base(ptr) { } + DeserializedNode& get() const { + return *static_cast<DeserializedNode*>(ptr); + } + +public: + static void construct(void* storage, DeserializedNode* ptr) { + new (storage) Concrete(ptr); + } + + CoarseType coarseType() const final { return get().coarseType; } + Id identifier() const override { return get().id; } + bool isLive() const override { return false; } + const char16_t* typeName() const override; + Node::Size size(mozilla::MallocSizeOf mallocSizeof) const override; + const char* jsObjectClassName() const override { return get().jsObjectClassName; } + const char* scriptFilename() const final { return get().scriptFilename; } + + bool hasAllocationStack() const override { return get().allocationStack.isSome(); } + StackFrame allocationStack() const override; + + // We ignore the `bool wantNames` parameter because we can't control whether + // the core dump was serialized with edge names or not. + js::UniquePtr<EdgeRange> edges(JSContext* cx, bool) const override; + + static const char16_t concreteTypeName[]; +}; + +template<> +class ConcreteStackFrame<DeserializedStackFrame> : public BaseStackFrame +{ +protected: + explicit ConcreteStackFrame(DeserializedStackFrame* ptr) + : BaseStackFrame(ptr) + { } + + DeserializedStackFrame& get() const { + return *static_cast<DeserializedStackFrame*>(ptr); + } + +public: + static void construct(void* storage, DeserializedStackFrame* ptr) { + new (storage) ConcreteStackFrame(ptr); + } + + uint64_t identifier() const override { return get().id; } + uint32_t line() const override { return get().line; } + uint32_t column() const override { return get().column; } + bool isSystem() const override { return get().isSystem; } + bool isSelfHosted(JSContext* cx) const override { return get().isSelfHosted; } + void trace(JSTracer* trc) override { } + AtomOrTwoByteChars source() const override { + return AtomOrTwoByteChars(get().source); + } + AtomOrTwoByteChars functionDisplayName() const override { + return AtomOrTwoByteChars(get().functionDisplayName); + } + + StackFrame parent() const override; + bool constructSavedFrameStack(JSContext* cx, + MutableHandleObject outSavedFrameStack) + const override; +}; + +} // namespace ubi +} // namespace JS + +#endif // mozilla_devtools_DeserializedNode__ diff --git a/dom/heapsnapshot/DominatorTree.cpp b/dom/heapsnapshot/DominatorTree.cpp new file mode 100644 index 000000000..e53c196cf --- /dev/null +++ b/dom/heapsnapshot/DominatorTree.cpp @@ -0,0 +1,140 @@ +/* -*- 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 "mozilla/devtools/DominatorTree.h" +#include "mozilla/dom/DominatorTreeBinding.h" + +namespace mozilla { +namespace devtools { + +dom::Nullable<uint64_t> +DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv) +{ + JS::ubi::Node::Id id(aNodeId); + auto node = mHeapSnapshot->getNodeById(id); + if (node.isNothing()) + return dom::Nullable<uint64_t>(); + + auto mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf(); + JS::ubi::Node::Size size = 0; + if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return dom::Nullable<uint64_t>(); + } + + MOZ_ASSERT(size != 0, + "The node should not have been unknown since we got it from the heap snapshot."); + return dom::Nullable<uint64_t>(size); +} + +struct NodeAndRetainedSize +{ + JS::ubi::Node mNode; + JS::ubi::Node::Size mSize; + + NodeAndRetainedSize(const JS::ubi::Node& aNode, JS::ubi::Node::Size aSize) + : mNode(aNode) + , mSize(aSize) + { } + + struct Comparator + { + static bool + Equals(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) + { + return aLhs.mSize == aRhs.mSize; + } + + static bool + LessThan(const NodeAndRetainedSize& aLhs, const NodeAndRetainedSize& aRhs) + { + // Use > because we want to sort from greatest to least retained size. + return aLhs.mSize > aRhs.mSize; + } + }; +}; + +void +DominatorTree::GetImmediatelyDominated(uint64_t aNodeId, + dom::Nullable<nsTArray<uint64_t>>& aOutResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(aOutResult.IsNull()); + + JS::ubi::Node::Id id(aNodeId); + Maybe<JS::ubi::Node> node = mHeapSnapshot->getNodeById(id); + if (node.isNothing()) + return; + + // Get all immediately dominated nodes and their retained sizes. + MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf(); + Maybe<JS::ubi::DominatorTree::DominatedSetRange> range = mDominatorTree.getDominatedSet(*node); + MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot."); + size_t length = range->length(); + nsTArray<NodeAndRetainedSize> dominatedNodes(length); + for (const JS::ubi::Node& dominatedNode : *range) { + JS::ubi::Node::Size retainedSize = 0; + if (NS_WARN_IF(!mDominatorTree.getRetainedSize(dominatedNode, mallocSizeOf, retainedSize))) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + MOZ_ASSERT(retainedSize != 0, + "retainedSize should not be zero since we know the node is in the dominator tree."); + + dominatedNodes.AppendElement(NodeAndRetainedSize(dominatedNode, retainedSize)); + } + + // Sort them by retained size. + NodeAndRetainedSize::Comparator comparator; + dominatedNodes.Sort(comparator); + + // Fill the result with the nodes' ids. + JS::ubi::Node root = mDominatorTree.root(); + aOutResult.SetValue(nsTArray<uint64_t>(length)); + for (const NodeAndRetainedSize& entry : dominatedNodes) { + // The root dominates itself, but we don't want to expose that to JS. + if (entry.mNode == root) + continue; + + aOutResult.Value().AppendElement(entry.mNode.identifier()); + } +} + +dom::Nullable<uint64_t> +DominatorTree::GetImmediateDominator(uint64_t aNodeId) const +{ + JS::ubi::Node::Id id(aNodeId); + Maybe<JS::ubi::Node> node = mHeapSnapshot->getNodeById(id); + if (node.isNothing()) + return dom::Nullable<uint64_t>(); + + JS::ubi::Node dominator = mDominatorTree.getImmediateDominator(*node); + if (!dominator || dominator == *node) + return dom::Nullable<uint64_t>(); + + return dom::Nullable<uint64_t>(dominator.identifier()); +} + + +/*** Cycle Collection Boilerplate *****************************************************************/ + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DominatorTree, mParent, mHeapSnapshot) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(DominatorTree) +NS_IMPL_CYCLE_COLLECTING_RELEASE(DominatorTree) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DominatorTree) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* virtual */ JSObject* +DominatorTree::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) +{ + return dom::DominatorTreeBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/DominatorTree.h b/dom/heapsnapshot/DominatorTree.h new file mode 100644 index 000000000..f785d4916 --- /dev/null +++ b/dom/heapsnapshot/DominatorTree.h @@ -0,0 +1,67 @@ +/* -*- 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_devtools_DominatorTree__ +#define mozilla_devtools_DominatorTree__ + +#include "mozilla/devtools/HeapSnapshot.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/RefCounted.h" +#include "js/UbiNodeDominatorTree.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace devtools { + +class DominatorTree final : public nsISupports + , public nsWrapperCache +{ +protected: + nsCOMPtr<nsISupports> mParent; + + virtual ~DominatorTree() { } + +private: + JS::ubi::DominatorTree mDominatorTree; + RefPtr<HeapSnapshot> mHeapSnapshot; + +public: + explicit DominatorTree(JS::ubi::DominatorTree&& aDominatorTree, HeapSnapshot* aHeapSnapshot, + nsISupports* aParent) + : mParent(aParent) + , mDominatorTree(Move(aDominatorTree)) + , mHeapSnapshot(aHeapSnapshot) + { + MOZ_ASSERT(aParent); + MOZ_ASSERT(aHeapSnapshot); + }; + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS; + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DominatorTree); + + nsISupports* GetParentObject() const { return mParent; } + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + // readonly attribute NodeId root + uint64_t Root() const { return mDominatorTree.root().identifier(); } + + // [Throws] NodeSize getRetainedSize(NodeId node) + dom::Nullable<uint64_t> GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv); + + // [Throws] sequence<NodeId>? getImmediatelyDominated(NodeId node); + void GetImmediatelyDominated(uint64_t aNodeId, dom::Nullable<nsTArray<uint64_t>>& aOutDominated, + ErrorResult& aRv); + + // NodeId? getImmediateDominator(NodeId node); + dom::Nullable<uint64_t> GetImmediateDominator(uint64_t aNodeId) const; +}; + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_DominatorTree__ diff --git a/dom/heapsnapshot/FileDescriptorOutputStream.cpp b/dom/heapsnapshot/FileDescriptorOutputStream.cpp new file mode 100644 index 000000000..72a289558 --- /dev/null +++ b/dom/heapsnapshot/FileDescriptorOutputStream.cpp @@ -0,0 +1,86 @@ +/* -*- 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 "mozilla/devtools/FileDescriptorOutputStream.h" +#include "private/pprio.h" + +namespace mozilla { +namespace devtools { + +/* static */ already_AddRefed<FileDescriptorOutputStream> +FileDescriptorOutputStream::Create(const ipc::FileDescriptor& fileDescriptor) +{ + if (NS_WARN_IF(!fileDescriptor.IsValid())) + return nullptr; + + auto rawFD = fileDescriptor.ClonePlatformHandle(); + PRFileDesc* prfd = PR_ImportFile(PROsfd(rawFD.release())); + if (NS_WARN_IF(!prfd)) + return nullptr; + + RefPtr<FileDescriptorOutputStream> stream = new FileDescriptorOutputStream(prfd); + return stream.forget(); +} + +NS_IMPL_ISUPPORTS(FileDescriptorOutputStream, nsIOutputStream); + +NS_IMETHODIMP +FileDescriptorOutputStream::Close() +{ + // Repeatedly closing is idempotent. + if (!fd) + return NS_OK; + + if (PR_Close(fd) != PR_SUCCESS) + return NS_ERROR_FAILURE; + fd = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +FileDescriptorOutputStream::Write(const char* buf, uint32_t count, uint32_t* retval) +{ + if (NS_WARN_IF(!fd)) + return NS_ERROR_FAILURE; + + auto written = PR_Write(fd, buf, count); + if (written < 0) + return NS_ERROR_FAILURE; + *retval = written; + return NS_OK; +} + +NS_IMETHODIMP +FileDescriptorOutputStream::Flush() +{ + if (NS_WARN_IF(!fd)) + return NS_ERROR_FAILURE; + + return PR_Sync(fd) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FileDescriptorOutputStream::WriteFrom(nsIInputStream* fromStream, uint32_t count, + uint32_t* retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorOutputStream::WriteSegments(nsReadSegmentFun reader, void* closure, + uint32_t count, uint32_t* retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +FileDescriptorOutputStream::IsNonBlocking(bool* retval) +{ + *retval = false; + return NS_OK; +} + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/FileDescriptorOutputStream.h b/dom/heapsnapshot/FileDescriptorOutputStream.h new file mode 100644 index 000000000..6990f1fc3 --- /dev/null +++ b/dom/heapsnapshot/FileDescriptorOutputStream.h @@ -0,0 +1,41 @@ +/* -*- 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_devtools_FileDescriptorOutputStream_h +#define mozilla_devtools_FileDescriptorOutputStream_h + +#include <prio.h> + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "nsIOutputStream.h" + +namespace mozilla { +namespace devtools { + +class FileDescriptorOutputStream final : public nsIOutputStream +{ +private: + PRFileDesc* fd; + +public: + static already_AddRefed<FileDescriptorOutputStream> + Create(const ipc::FileDescriptor& fileDescriptor); + +private: + explicit FileDescriptorOutputStream(PRFileDesc* prfd) + : fd(prfd) + { } + + virtual ~FileDescriptorOutputStream() { } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAM +}; + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_FileDescriptorOutputStream_h diff --git a/dom/heapsnapshot/HeapSnapshot.cpp b/dom/heapsnapshot/HeapSnapshot.cpp new file mode 100644 index 000000000..299a96a9c --- /dev/null +++ b/dom/heapsnapshot/HeapSnapshot.cpp @@ -0,0 +1,1619 @@ +/* -*- 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 "HeapSnapshot.h" + +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/gzip_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> + +#include "js/Debug.h" +#include "js/TypeDecls.h" +#include "js/UbiNodeBreadthFirst.h" +#include "js/UbiNodeCensus.h" +#include "js/UbiNodeDominatorTree.h" +#include "js/UbiNodeShortestPaths.h" +#include "mozilla/Attributes.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/devtools/AutoMemMap.h" +#include "mozilla/devtools/CoreDump.pb.h" +#include "mozilla/devtools/DeserializedNode.h" +#include "mozilla/devtools/DominatorTree.h" +#include "mozilla/devtools/FileDescriptorOutputStream.h" +#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h" +#include "mozilla/devtools/ZeroCopyNSIOutputStream.h" +#include "mozilla/dom/ChromeUtils.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/HeapSnapshotBinding.h" +#include "mozilla/RangedPtr.h" +#include "mozilla/Unused.h" + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "nsCycleCollectionParticipant.h" +#include "nsCRTGlue.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIFile.h" +#include "nsIOutputStream.h" +#include "nsISupportsImpl.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "prerror.h" +#include "prio.h" +#include "prtypes.h" + +namespace mozilla { +namespace devtools { + +using namespace JS; +using namespace dom; + +using ::google::protobuf::io::ArrayInputStream; +using ::google::protobuf::io::CodedInputStream; +using ::google::protobuf::io::GzipInputStream; +using ::google::protobuf::io::ZeroCopyInputStream; + +using JS::ubi::AtomOrTwoByteChars; +using JS::ubi::ShortestPaths; + +MallocSizeOf +GetCurrentThreadDebuggerMallocSizeOf() +{ + auto ccjscx = CycleCollectedJSContext::Get(); + MOZ_ASSERT(ccjscx); + auto cx = ccjscx->Context(); + MOZ_ASSERT(cx); + auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(cx); + MOZ_ASSERT(mallocSizeOf); + return mallocSizeOf; +} + +/*** Cycle Collection Boilerplate *****************************************************************/ + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HeapSnapshot, mParent) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(HeapSnapshot) +NS_IMPL_CYCLE_COLLECTING_RELEASE(HeapSnapshot) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HeapSnapshot) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +/* virtual */ JSObject* +HeapSnapshot::WrapObject(JSContext* aCx, HandleObject aGivenProto) +{ + return HeapSnapshotBinding::Wrap(aCx, this, aGivenProto); +} + +/*** Reading Heap Snapshots ***********************************************************************/ + +/* static */ already_AddRefed<HeapSnapshot> +HeapSnapshot::Create(JSContext* cx, + GlobalObject& global, + const uint8_t* buffer, + uint32_t size, + ErrorResult& rv) +{ + RefPtr<HeapSnapshot> snapshot = new HeapSnapshot(cx, global.GetAsSupports()); + if (!snapshot->init(cx, buffer, size)) { + rv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + return snapshot.forget(); +} + +template<typename MessageType> +static bool +parseMessage(ZeroCopyInputStream& stream, uint32_t sizeOfMessage, MessageType& message) +{ + // We need to create a new `CodedInputStream` for each message so that the + // 64MB limit is applied per-message rather than to the whole stream. + CodedInputStream codedStream(&stream); + + // The protobuf message nesting that core dumps exhibit is dominated by + // allocation stacks' frames. In the most deeply nested case, each frame has + // two messages: a StackFrame message and a StackFrame::Data message. These + // frames are on top of a small constant of other messages. There are a + // MAX_STACK_DEPTH number of frames, so we multiply this by 3 to make room for + // the two messages per frame plus some head room for the constant number of + // non-dominating messages. + codedStream.SetRecursionLimit(HeapSnapshot::MAX_STACK_DEPTH * 3); + + auto limit = codedStream.PushLimit(sizeOfMessage); + if (NS_WARN_IF(!message.ParseFromCodedStream(&codedStream)) || + NS_WARN_IF(!codedStream.ConsumedEntireMessage()) || + NS_WARN_IF(codedStream.BytesUntilLimit() != 0)) + { + return false; + } + + codedStream.PopLimit(limit); + return true; +} + +template<typename CharT, typename InternedStringSet> +struct GetOrInternStringMatcher +{ + InternedStringSet& internedStrings; + + explicit GetOrInternStringMatcher(InternedStringSet& strings) : internedStrings(strings) { } + + const CharT* match(const std::string* str) { + MOZ_ASSERT(str); + size_t length = str->length() / sizeof(CharT); + auto tempString = reinterpret_cast<const CharT*>(str->data()); + + UniquePtr<CharT[], NSFreePolicy> owned(NS_strndup(tempString, length)); + if (!owned || !internedStrings.append(Move(owned))) + return nullptr; + + return internedStrings.back().get(); + } + + const CharT* match(uint64_t ref) { + if (MOZ_LIKELY(ref < internedStrings.length())) { + auto& string = internedStrings[ref]; + MOZ_ASSERT(string); + return string.get(); + } + + return nullptr; + } +}; + +template< + // Either char or char16_t. + typename CharT, + // A reference to either `internedOneByteStrings` or `internedTwoByteStrings` + // if CharT is char or char16_t respectively. + typename InternedStringSet> +const CharT* +HeapSnapshot::getOrInternString(InternedStringSet& internedStrings, + Maybe<StringOrRef>& maybeStrOrRef) +{ + // Incomplete message: has neither a string nor a reference to an already + // interned string. + if (MOZ_UNLIKELY(maybeStrOrRef.isNothing())) + return nullptr; + + GetOrInternStringMatcher<CharT, InternedStringSet> m(internedStrings); + return maybeStrOrRef->match(m); +} + +// Get a de-duplicated string as a Maybe<StringOrRef> from the given `msg`. +#define GET_STRING_OR_REF_WITH_PROP_NAMES(msg, strPropertyName, refPropertyName) \ + (msg.has_##refPropertyName() \ + ? Some(StringOrRef(msg.refPropertyName())) \ + : msg.has_##strPropertyName() \ + ? Some(StringOrRef(&msg.strPropertyName())) \ + : Nothing()) + +#define GET_STRING_OR_REF(msg, property) \ + (msg.has_##property##ref() \ + ? Some(StringOrRef(msg.property##ref())) \ + : msg.has_##property() \ + ? Some(StringOrRef(&msg.property())) \ + : Nothing()) + +bool +HeapSnapshot::saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents) +{ + // NB: de-duplicated string properties must be read back and interned in the + // same order here as they are written and serialized in + // `CoreDumpWriter::writeNode` or else indices in references to already + // serialized strings will be off. + + if (NS_WARN_IF(!node.has_id())) + return false; + NodeId id = node.id(); + + // NodeIds are derived from pointers (at most 48 bits) and we rely on them + // fitting into JS numbers (IEEE 754 doubles, can precisely store 53 bit + // integers) despite storing them on disk as 64 bit integers. + if (NS_WARN_IF(!JS::Value::isNumberRepresentable(id))) + return false; + + // Should only deserialize each node once. + if (NS_WARN_IF(nodes.has(id))) + return false; + + if (NS_WARN_IF(!JS::ubi::Uint32IsValidCoarseType(node.coarsetype()))) + return false; + auto coarseType = JS::ubi::Uint32ToCoarseType(node.coarsetype()); + + Maybe<StringOrRef> typeNameOrRef = GET_STRING_OR_REF_WITH_PROP_NAMES(node, typename_, typenameref); + auto typeName = getOrInternString<char16_t>(internedTwoByteStrings, typeNameOrRef); + if (NS_WARN_IF(!typeName)) + return false; + + if (NS_WARN_IF(!node.has_size())) + return false; + uint64_t size = node.size(); + + auto edgesLength = node.edges_size(); + DeserializedNode::EdgeVector edges; + if (NS_WARN_IF(!edges.reserve(edgesLength))) + return false; + for (decltype(edgesLength) i = 0; i < edgesLength; i++) { + auto& protoEdge = node.edges(i); + + if (NS_WARN_IF(!protoEdge.has_referent())) + return false; + NodeId referent = protoEdge.referent(); + + if (NS_WARN_IF(!edgeReferents.put(referent))) + return false; + + const char16_t* edgeName = nullptr; + if (protoEdge.EdgeNameOrRef_case() != protobuf::Edge::EDGENAMEORREF_NOT_SET) { + Maybe<StringOrRef> edgeNameOrRef = GET_STRING_OR_REF(protoEdge, name); + edgeName = getOrInternString<char16_t>(internedTwoByteStrings, edgeNameOrRef); + if (NS_WARN_IF(!edgeName)) + return false; + } + + edges.infallibleAppend(DeserializedEdge(referent, edgeName)); + } + + Maybe<StackFrameId> allocationStack; + if (node.has_allocationstack()) { + StackFrameId id = 0; + if (NS_WARN_IF(!saveStackFrame(node.allocationstack(), id))) + return false; + allocationStack.emplace(id); + } + MOZ_ASSERT(allocationStack.isSome() == node.has_allocationstack()); + + const char* jsObjectClassName = nullptr; + if (node.JSObjectClassNameOrRef_case() != protobuf::Node::JSOBJECTCLASSNAMEORREF_NOT_SET) { + Maybe<StringOrRef> clsNameOrRef = GET_STRING_OR_REF(node, jsobjectclassname); + jsObjectClassName = getOrInternString<char>(internedOneByteStrings, clsNameOrRef); + if (NS_WARN_IF(!jsObjectClassName)) + return false; + } + + const char* scriptFilename = nullptr; + if (node.ScriptFilenameOrRef_case() != protobuf::Node::SCRIPTFILENAMEORREF_NOT_SET) { + Maybe<StringOrRef> scriptFilenameOrRef = GET_STRING_OR_REF(node, scriptfilename); + scriptFilename = getOrInternString<char>(internedOneByteStrings, scriptFilenameOrRef); + if (NS_WARN_IF(!scriptFilename)) + return false; + } + + if (NS_WARN_IF(!nodes.putNew(id, DeserializedNode(id, coarseType, typeName, + size, Move(edges), + allocationStack, + jsObjectClassName, + scriptFilename, *this)))) + { + return false; + }; + + return true; +} + +bool +HeapSnapshot::saveStackFrame(const protobuf::StackFrame& frame, + StackFrameId& outFrameId) +{ + // NB: de-duplicated string properties must be read in the same order here as + // they are written in `CoreDumpWriter::getProtobufStackFrame` or else indices + // in references to already serialized strings will be off. + + if (frame.has_ref()) { + // We should only get a reference to the previous frame if we have already + // seen the previous frame. + if (!frames.has(frame.ref())) + return false; + + outFrameId = frame.ref(); + return true; + } + + // Incomplete message. + if (!frame.has_data()) + return false; + + auto data = frame.data(); + + if (!data.has_id()) + return false; + StackFrameId id = data.id(); + + // This should be the first and only time we see this frame. + if (frames.has(id)) + return false; + + if (!data.has_line()) + return false; + uint32_t line = data.line(); + + if (!data.has_column()) + return false; + uint32_t column = data.column(); + + if (!data.has_issystem()) + return false; + bool isSystem = data.issystem(); + + if (!data.has_isselfhosted()) + return false; + bool isSelfHosted = data.isselfhosted(); + + Maybe<StringOrRef> sourceOrRef = GET_STRING_OR_REF(data, source); + auto source = getOrInternString<char16_t>(internedTwoByteStrings, sourceOrRef); + if (!source) + return false; + + const char16_t* functionDisplayName = nullptr; + if (data.FunctionDisplayNameOrRef_case() != + protobuf::StackFrame_Data::FUNCTIONDISPLAYNAMEORREF_NOT_SET) + { + Maybe<StringOrRef> nameOrRef = GET_STRING_OR_REF(data, functiondisplayname); + functionDisplayName = getOrInternString<char16_t>(internedTwoByteStrings, nameOrRef); + if (!functionDisplayName) + return false; + } + + Maybe<StackFrameId> parent; + if (data.has_parent()) { + StackFrameId parentId = 0; + if (!saveStackFrame(data.parent(), parentId)) + return false; + parent = Some(parentId); + } + + if (!frames.putNew(id, DeserializedStackFrame(id, parent, line, column, + source, functionDisplayName, + isSystem, isSelfHosted, *this))) + { + return false; + } + + outFrameId = id; + return true; +} + +#undef GET_STRING_OR_REF_WITH_PROP_NAMES +#undef GET_STRING_OR_REF + +// Because protobuf messages aren't self-delimiting, we serialize each message +// preceded by its size in bytes. When deserializing, we read this size and then +// limit reading from the stream to the given byte size. If we didn't, then the +// first message would consume the entire stream. +static bool +readSizeOfNextMessage(ZeroCopyInputStream& stream, uint32_t* sizep) +{ + MOZ_ASSERT(sizep); + CodedInputStream codedStream(&stream); + return codedStream.ReadVarint32(sizep) && *sizep > 0; +} + +bool +HeapSnapshot::init(JSContext* cx, const uint8_t* buffer, uint32_t size) +{ + if (!nodes.init() || !frames.init()) + return false; + + ArrayInputStream stream(buffer, size); + GzipInputStream gzipStream(&stream); + uint32_t sizeOfMessage = 0; + + // First is the metadata. + + protobuf::Metadata metadata; + if (NS_WARN_IF(!readSizeOfNextMessage(gzipStream, &sizeOfMessage))) + return false; + if (!parseMessage(gzipStream, sizeOfMessage, metadata)) + return false; + if (metadata.has_timestamp()) + timestamp.emplace(metadata.timestamp()); + + // Next is the root node. + + protobuf::Node root; + if (NS_WARN_IF(!readSizeOfNextMessage(gzipStream, &sizeOfMessage))) + return false; + if (!parseMessage(gzipStream, sizeOfMessage, root)) + return false; + + // Although the id is optional in the protobuf format for future proofing, we + // can't currently do anything without it. + if (NS_WARN_IF(!root.has_id())) + return false; + rootId = root.id(); + + // The set of all node ids we've found edges pointing to. + NodeIdSet edgeReferents(cx); + if (NS_WARN_IF(!edgeReferents.init())) + return false; + + if (NS_WARN_IF(!saveNode(root, edgeReferents))) + return false; + + // Finally, the rest of the nodes in the core dump. + + // Test for the end of the stream. The protobuf library gives no way to tell + // the difference between an underlying read error and the stream being + // done. All we can do is attempt to read the size of the next message and + // extrapolate guestimations from the result of that operation. + while (readSizeOfNextMessage(gzipStream, &sizeOfMessage)) { + protobuf::Node node; + if (!parseMessage(gzipStream, sizeOfMessage, node)) + return false; + if (NS_WARN_IF(!saveNode(node, edgeReferents))) + return false; + } + + // Check the set of node ids referred to by edges we found and ensure that we + // have the node corresponding to each id. If we don't have all of them, it is + // unsafe to perform analyses of this heap snapshot. + for (auto range = edgeReferents.all(); !range.empty(); range.popFront()) { + if (NS_WARN_IF(!nodes.has(range.front()))) + return false; + } + + return true; +} + + +/*** Heap Snapshot Analyses ***********************************************************************/ + +void +HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options, + JS::MutableHandleValue rval, ErrorResult& rv) +{ + JS::ubi::Census census(cx); + if (NS_WARN_IF(!census.init())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + JS::ubi::CountTypePtr rootType; + if (NS_WARN_IF(!JS::ubi::ParseCensusOptions(cx, census, options, rootType))) { + rv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + JS::ubi::RootedCount rootCount(cx, rootType->makeCount()); + if (NS_WARN_IF(!rootCount)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + JS::ubi::CensusHandler handler(census, rootCount, GetCurrentThreadDebuggerMallocSizeOf()); + + { + JS::AutoCheckCannotGC nogc; + + JS::ubi::CensusTraversal traversal(cx, handler, nogc); + if (NS_WARN_IF(!traversal.init())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + if (NS_WARN_IF(!traversal.addStart(getRoot()))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + if (NS_WARN_IF(!traversal.traverse())) { + rv.Throw(NS_ERROR_UNEXPECTED); + return; + } + } + + if (NS_WARN_IF(!handler.report(cx, rval))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } +} + +void +HeapSnapshot::DescribeNode(JSContext* cx, JS::HandleObject breakdown, uint64_t nodeId, + JS::MutableHandleValue rval, ErrorResult& rv) { + MOZ_ASSERT(breakdown); + JS::RootedValue breakdownVal(cx, JS::ObjectValue(*breakdown)); + JS::ubi::CountTypePtr rootType = JS::ubi::ParseBreakdown(cx, breakdownVal); + if (NS_WARN_IF(!rootType)) { + rv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + JS::ubi::RootedCount rootCount(cx, rootType->makeCount()); + if (NS_WARN_IF(!rootCount)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + JS::ubi::Node::Id id(nodeId); + Maybe<JS::ubi::Node> node = getNodeById(id); + if (NS_WARN_IF(node.isNothing())) { + rv.Throw(NS_ERROR_INVALID_ARG); + return; + } + + MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf(); + if (NS_WARN_IF(!rootCount->count(mallocSizeOf, *node))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + if (NS_WARN_IF(!rootCount->report(cx, rval))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } +} + + +already_AddRefed<DominatorTree> +HeapSnapshot::ComputeDominatorTree(ErrorResult& rv) +{ + Maybe<JS::ubi::DominatorTree> maybeTree; + { + auto ccjscx = CycleCollectedJSContext::Get(); + MOZ_ASSERT(ccjscx); + auto cx = ccjscx->Context(); + MOZ_ASSERT(cx); + JS::AutoCheckCannotGC nogc(cx); + maybeTree = JS::ubi::DominatorTree::Create(cx, nogc, getRoot()); + } + + if (NS_WARN_IF(maybeTree.isNothing())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + + return MakeAndAddRef<DominatorTree>(Move(*maybeTree), this, mParent); +} + +void +HeapSnapshot::ComputeShortestPaths(JSContext*cx, uint64_t start, + const Sequence<uint64_t>& targets, + uint64_t maxNumPaths, + JS::MutableHandleObject results, + ErrorResult& rv) +{ + // First ensure that our inputs are valid. + + if (NS_WARN_IF(maxNumPaths == 0)) { + rv.Throw(NS_ERROR_INVALID_ARG); + return; + } + + Maybe<JS::ubi::Node> startNode = getNodeById(start); + if (NS_WARN_IF(startNode.isNothing())) { + rv.Throw(NS_ERROR_INVALID_ARG); + return; + } + + if (NS_WARN_IF(targets.Length() == 0)) { + rv.Throw(NS_ERROR_INVALID_ARG); + return; + } + + // Aggregate the targets into a set and make sure that they exist in the heap + // snapshot. + + JS::ubi::NodeSet targetsSet; + if (NS_WARN_IF(!targetsSet.init())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + for (const auto& target : targets) { + Maybe<JS::ubi::Node> targetNode = getNodeById(target); + if (NS_WARN_IF(targetNode.isNothing())) { + rv.Throw(NS_ERROR_INVALID_ARG); + return; + } + + if (NS_WARN_IF(!targetsSet.put(*targetNode))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } + + // Walk the heap graph and find the shortest paths. + + Maybe<ShortestPaths> maybeShortestPaths; + { + JS::AutoCheckCannotGC nogc(cx); + maybeShortestPaths = ShortestPaths::Create(cx, nogc, maxNumPaths, *startNode, + Move(targetsSet)); + } + + if (NS_WARN_IF(maybeShortestPaths.isNothing())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + auto& shortestPaths = *maybeShortestPaths; + + // Convert the results into a Map object mapping target node IDs to arrays of + // paths found. + + RootedObject resultsMap(cx, JS::NewMapObject(cx)); + if (NS_WARN_IF(!resultsMap)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + for (auto range = shortestPaths.eachTarget(); !range.empty(); range.popFront()) { + JS::RootedValue key(cx, JS::NumberValue(range.front().identifier())); + JS::AutoValueVector paths(cx); + + bool ok = shortestPaths.forEachPath(range.front(), [&](JS::ubi::Path& path) { + JS::AutoValueVector pathValues(cx); + + for (JS::ubi::BackEdge* edge : path) { + JS::RootedObject pathPart(cx, JS_NewPlainObject(cx)); + if (!pathPart) { + return false; + } + + JS::RootedValue predecessor(cx, NumberValue(edge->predecessor().identifier())); + if (!JS_DefineProperty(cx, pathPart, "predecessor", predecessor, JSPROP_ENUMERATE)) { + return false; + } + + RootedValue edgeNameVal(cx, NullValue()); + if (edge->name()) { + RootedString edgeName(cx, JS_AtomizeUCString(cx, edge->name().get())); + if (!edgeName) { + return false; + } + edgeNameVal = StringValue(edgeName); + } + + if (!JS_DefineProperty(cx, pathPart, "edge", edgeNameVal, JSPROP_ENUMERATE)) { + return false; + } + + if (!pathValues.append(ObjectValue(*pathPart))) { + return false; + } + } + + RootedObject pathObj(cx, JS_NewArrayObject(cx, pathValues)); + return pathObj && paths.append(ObjectValue(*pathObj)); + }); + + if (NS_WARN_IF(!ok)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + JS::RootedObject pathsArray(cx, JS_NewArrayObject(cx, paths)); + if (NS_WARN_IF(!pathsArray)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + JS::RootedValue pathsVal(cx, ObjectValue(*pathsArray)); + if (NS_WARN_IF(!JS::MapSet(cx, resultsMap, key, pathsVal))) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } + + results.set(resultsMap); +} + +/*** Saving Heap Snapshots ************************************************************************/ + +// If we are only taking a snapshot of the heap affected by the given set of +// globals, find the set of compartments the globals are allocated +// within. Returns false on OOM failure. +static bool +PopulateCompartmentsWithGlobals(CompartmentSet& compartments, AutoObjectVector& globals) +{ + if (!compartments.init()) + return false; + + unsigned length = globals.length(); + for (unsigned i = 0; i < length; i++) { + if (!compartments.put(GetObjectCompartment(globals[i]))) + return false; + } + + return true; +} + +// Add the given set of globals as explicit roots in the given roots +// list. Returns false on OOM failure. +static bool +AddGlobalsAsRoots(AutoObjectVector& globals, ubi::RootList& roots) +{ + unsigned length = globals.length(); + for (unsigned i = 0; i < length; i++) { + if (!roots.addRoot(ubi::Node(globals[i].get()), + u"heap snapshot global")) + { + return false; + } + } + return true; +} + +// Choose roots and limits for a traversal, given `boundaries`. Set `roots` to +// the set of nodes within the boundaries that are referred to by nodes +// outside. If `boundaries` does not include all JS compartments, initialize +// `compartments` to the set of included compartments; otherwise, leave +// `compartments` uninitialized. (You can use compartments.initialized() to +// check.) +// +// If `boundaries` is incoherent, or we encounter an error while trying to +// handle it, or we run out of memory, set `rv` appropriately and return +// `false`. +static bool +EstablishBoundaries(JSContext* cx, + ErrorResult& rv, + const HeapSnapshotBoundaries& boundaries, + ubi::RootList& roots, + CompartmentSet& compartments) +{ + MOZ_ASSERT(!roots.initialized()); + MOZ_ASSERT(!compartments.initialized()); + + bool foundBoundaryProperty = false; + + if (boundaries.mRuntime.WasPassed()) { + foundBoundaryProperty = true; + + if (!boundaries.mRuntime.Value()) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + + if (!roots.init()) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return false; + } + } + + if (boundaries.mDebugger.WasPassed()) { + if (foundBoundaryProperty) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + foundBoundaryProperty = true; + + JSObject* dbgObj = boundaries.mDebugger.Value(); + if (!dbgObj || !dbg::IsDebugger(*dbgObj)) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + + AutoObjectVector globals(cx); + if (!dbg::GetDebuggeeGlobals(cx, *dbgObj, globals) || + !PopulateCompartmentsWithGlobals(compartments, globals) || + !roots.init(compartments) || + !AddGlobalsAsRoots(globals, roots)) + { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return false; + } + } + + if (boundaries.mGlobals.WasPassed()) { + if (foundBoundaryProperty) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + foundBoundaryProperty = true; + + uint32_t length = boundaries.mGlobals.Value().Length(); + if (length == 0) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + + AutoObjectVector globals(cx); + for (uint32_t i = 0; i < length; i++) { + JSObject* global = boundaries.mGlobals.Value().ElementAt(i); + if (!JS_IsGlobalObject(global)) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + if (!globals.append(global)) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return false; + } + } + + if (!PopulateCompartmentsWithGlobals(compartments, globals) || + !roots.init(compartments) || + !AddGlobalsAsRoots(globals, roots)) + { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return false; + } + } + + if (!foundBoundaryProperty) { + rv.Throw(NS_ERROR_INVALID_ARG); + return false; + } + + MOZ_ASSERT(roots.initialized()); + MOZ_ASSERT_IF(boundaries.mDebugger.WasPassed(), compartments.initialized()); + MOZ_ASSERT_IF(boundaries.mGlobals.WasPassed(), compartments.initialized()); + return true; +} + + +// A variant covering all the various two-byte strings that we can get from the +// ubi::Node API. +class TwoByteString : public Variant<JSAtom*, const char16_t*, JS::ubi::EdgeName> +{ + using Base = Variant<JSAtom*, const char16_t*, JS::ubi::EdgeName>; + + struct AsTwoByteStringMatcher + { + TwoByteString match(JSAtom* atom) { + return TwoByteString(atom); + } + + TwoByteString match(const char16_t* chars) { + return TwoByteString(chars); + } + }; + + struct IsNonNullMatcher + { + template<typename T> + bool match(const T& t) { return t != nullptr; } + }; + + struct LengthMatcher + { + size_t match(JSAtom* atom) { + MOZ_ASSERT(atom); + JS::ubi::AtomOrTwoByteChars s(atom); + return s.length(); + } + + size_t match(const char16_t* chars) { + MOZ_ASSERT(chars); + return NS_strlen(chars); + } + + size_t match(const JS::ubi::EdgeName& ptr) { + MOZ_ASSERT(ptr); + return NS_strlen(ptr.get()); + } + }; + + struct CopyToBufferMatcher + { + RangedPtr<char16_t> destination; + size_t maxLength; + + CopyToBufferMatcher(RangedPtr<char16_t> destination, size_t maxLength) + : destination(destination) + , maxLength(maxLength) + { } + + size_t match(JS::ubi::EdgeName& ptr) { + return ptr ? match(ptr.get()) : 0; + } + + size_t match(JSAtom* atom) { + MOZ_ASSERT(atom); + JS::ubi::AtomOrTwoByteChars s(atom); + return s.copyToBuffer(destination, maxLength); + } + + size_t match(const char16_t* chars) { + MOZ_ASSERT(chars); + JS::ubi::AtomOrTwoByteChars s(chars); + return s.copyToBuffer(destination, maxLength); + } + }; + +public: + template<typename T> + MOZ_IMPLICIT TwoByteString(T&& rhs) : Base(Forward<T>(rhs)) { } + + template<typename T> + TwoByteString& operator=(T&& rhs) { + MOZ_ASSERT(this != &rhs, "self-move disallowed"); + this->~TwoByteString(); + new (this) TwoByteString(Forward<T>(rhs)); + return *this; + } + + TwoByteString(const TwoByteString&) = delete; + TwoByteString& operator=(const TwoByteString&) = delete; + + // Rewrap the inner value of a JS::ubi::AtomOrTwoByteChars as a TwoByteString. + static TwoByteString from(JS::ubi::AtomOrTwoByteChars&& s) { + AsTwoByteStringMatcher m; + return s.match(m); + } + + // Returns true if the given TwoByteString is non-null, false otherwise. + bool isNonNull() const { + IsNonNullMatcher m; + return match(m); + } + + // Return the length of the string, 0 if it is null. + size_t length() const { + LengthMatcher m; + return match(m); + } + + // Copy the contents of a TwoByteString into the provided buffer. The buffer + // is NOT null terminated. The number of characters written is returned. + size_t copyToBuffer(RangedPtr<char16_t> destination, size_t maxLength) { + CopyToBufferMatcher m(destination, maxLength); + return match(m); + } + + struct HashPolicy; +}; + +// A hashing policy for TwoByteString. +// +// Atoms are pointer hashed and use pointer equality, which means that we +// tolerate some duplication across atoms and the other two types of two-byte +// strings. In practice, we expect the amount of this duplication to be very low +// because each type is generally a different semantic thing in addition to +// having a slightly different representation. For example, the set of edge +// names and the set stack frames' source names naturally tend not to overlap +// very much if at all. +struct TwoByteString::HashPolicy { + using Lookup = TwoByteString; + + struct HashingMatcher { + js::HashNumber match(const JSAtom* atom) { + return js::DefaultHasher<const JSAtom*>::hash(atom); + } + + js::HashNumber match(const char16_t* chars) { + MOZ_ASSERT(chars); + auto length = NS_strlen(chars); + return HashString(chars, length); + } + + js::HashNumber match(const JS::ubi::EdgeName& ptr) { + MOZ_ASSERT(ptr); + return match(ptr.get()); + } + }; + + static js::HashNumber hash(const Lookup& l) { + HashingMatcher hasher; + return l.match(hasher); + } + + struct EqualityMatcher { + const TwoByteString& rhs; + explicit EqualityMatcher(const TwoByteString& rhs) : rhs(rhs) { } + + bool match(const JSAtom* atom) { + return rhs.is<JSAtom*>() && rhs.as<JSAtom*>() == atom; + } + + bool match(const char16_t* chars) { + MOZ_ASSERT(chars); + + const char16_t* rhsChars = nullptr; + if (rhs.is<const char16_t*>()) + rhsChars = rhs.as<const char16_t*>(); + else if (rhs.is<JS::ubi::EdgeName>()) + rhsChars = rhs.as<JS::ubi::EdgeName>().get(); + else + return false; + MOZ_ASSERT(rhsChars); + + auto length = NS_strlen(chars); + if (NS_strlen(rhsChars) != length) + return false; + + return memcmp(chars, rhsChars, length * sizeof(char16_t)) == 0; + } + + bool match(const JS::ubi::EdgeName& ptr) { + MOZ_ASSERT(ptr); + return match(ptr.get()); + } + }; + + static bool match(const TwoByteString& k, const Lookup& l) { + EqualityMatcher eq(l); + return k.match(eq); + } + + static void rekey(TwoByteString& k, TwoByteString&& newKey) { + k = Move(newKey); + } +}; + +// Returns whether `edge` should be included in a heap snapshot of +// `compartments`. The optional `policy` out-param is set to INCLUDE_EDGES +// if we want to include the referent's edges, or EXCLUDE_EDGES if we don't +// want to include them. +static bool +ShouldIncludeEdge(JS::CompartmentSet* compartments, + const ubi::Node& origin, const ubi::Edge& edge, + CoreDumpWriter::EdgePolicy* policy = nullptr) +{ + if (policy) { + *policy = CoreDumpWriter::INCLUDE_EDGES; + } + + if (!compartments) { + // We aren't targeting a particular set of compartments, so serialize all the + // things! + return true; + } + + // We are targeting a particular set of compartments. If this node is in our target + // set, serialize it and all of its edges. If this node is _not_ in our + // target set, we also serialize under the assumption that it is a shared + // resource being used by something in our target compartments since we reached it + // by traversing the heap graph. However, we do not serialize its outgoing + // edges and we abandon further traversal from this node. + // + // If the node does not belong to any compartment, we also serialize its outgoing + // edges. This case is relevant for Shapes: they don't belong to a specific + // compartment and contain edges to parent/kids Shapes we want to include. Note + // that these Shapes may contain pointers into our target compartment (the + // Shape's getter/setter JSObjects). However, we do not serialize nodes in other + // compartments that are reachable from these non-compartment nodes. + + JSCompartment* compartment = edge.referent.compartment(); + + if (!compartment || compartments->has(compartment)) { + return true; + } + + if (policy) { + *policy = CoreDumpWriter::EXCLUDE_EDGES; + } + + return !!origin.compartment(); +} + +// A `CoreDumpWriter` that serializes nodes to protobufs and writes them to the +// given `ZeroCopyOutputStream`. +class MOZ_STACK_CLASS StreamWriter : public CoreDumpWriter +{ + using FrameSet = js::HashSet<uint64_t>; + using TwoByteStringMap = js::HashMap<TwoByteString, uint64_t, TwoByteString::HashPolicy>; + using OneByteStringMap = js::HashMap<const char*, uint64_t>; + + JSContext* cx; + bool wantNames; + // The set of |JS::ubi::StackFrame::identifier()|s that have already been + // serialized and written to the core dump. + FrameSet framesAlreadySerialized; + // The set of two-byte strings that have already been serialized and written + // to the core dump. + TwoByteStringMap twoByteStringsAlreadySerialized; + // The set of one-byte strings that have already been serialized and written + // to the core dump. + OneByteStringMap oneByteStringsAlreadySerialized; + + ::google::protobuf::io::ZeroCopyOutputStream& stream; + + JS::CompartmentSet* compartments; + + bool writeMessage(const ::google::protobuf::MessageLite& message) { + // We have to create a new CodedOutputStream when writing each message so + // that the 64MB size limit used by Coded{Output,Input}Stream to prevent + // integer overflow is enforced per message rather than on the whole stream. + ::google::protobuf::io::CodedOutputStream codedStream(&stream); + codedStream.WriteVarint32(message.ByteSize()); + message.SerializeWithCachedSizes(&codedStream); + return !codedStream.HadError(); + } + + // Attach the full two-byte string or a reference to a two-byte string that + // has already been serialized to a protobuf message. + template <typename SetStringFunction, + typename SetRefFunction> + bool attachTwoByteString(TwoByteString& string, SetStringFunction setString, + SetRefFunction setRef) { + auto ptr = twoByteStringsAlreadySerialized.lookupForAdd(string); + if (ptr) { + setRef(ptr->value()); + return true; + } + + auto length = string.length(); + auto stringData = MakeUnique<std::string>(length * sizeof(char16_t), '\0'); + if (!stringData) + return false; + + auto buf = const_cast<char16_t*>(reinterpret_cast<const char16_t*>(stringData->data())); + string.copyToBuffer(RangedPtr<char16_t>(buf, length), length); + + uint64_t ref = twoByteStringsAlreadySerialized.count(); + if (!twoByteStringsAlreadySerialized.add(ptr, Move(string), ref)) + return false; + + setString(stringData.release()); + return true; + } + + // Attach the full one-byte string or a reference to a one-byte string that + // has already been serialized to a protobuf message. + template <typename SetStringFunction, + typename SetRefFunction> + bool attachOneByteString(const char* string, SetStringFunction setString, + SetRefFunction setRef) { + auto ptr = oneByteStringsAlreadySerialized.lookupForAdd(string); + if (ptr) { + setRef(ptr->value()); + return true; + } + + auto length = strlen(string); + auto stringData = MakeUnique<std::string>(string, length); + if (!stringData) + return false; + + uint64_t ref = oneByteStringsAlreadySerialized.count(); + if (!oneByteStringsAlreadySerialized.add(ptr, string, ref)) + return false; + + setString(stringData.release()); + return true; + } + + protobuf::StackFrame* getProtobufStackFrame(JS::ubi::StackFrame& frame, + size_t depth = 1) { + // NB: de-duplicated string properties must be written in the same order + // here as they are read in `HeapSnapshot::saveStackFrame` or else indices + // in references to already serialized strings will be off. + + MOZ_ASSERT(frame, + "null frames should be represented as the lack of a serialized " + "stack frame"); + + auto id = frame.identifier(); + auto protobufStackFrame = MakeUnique<protobuf::StackFrame>(); + if (!protobufStackFrame) + return nullptr; + + if (framesAlreadySerialized.has(id)) { + protobufStackFrame->set_ref(id); + return protobufStackFrame.release(); + } + + auto data = MakeUnique<protobuf::StackFrame_Data>(); + if (!data) + return nullptr; + + data->set_id(id); + data->set_line(frame.line()); + data->set_column(frame.column()); + data->set_issystem(frame.isSystem()); + data->set_isselfhosted(frame.isSelfHosted(cx)); + + auto dupeSource = TwoByteString::from(frame.source()); + if (!attachTwoByteString(dupeSource, + [&] (std::string* source) { data->set_allocated_source(source); }, + [&] (uint64_t ref) { data->set_sourceref(ref); })) + { + return nullptr; + } + + auto dupeName = TwoByteString::from(frame.functionDisplayName()); + if (dupeName.isNonNull()) { + if (!attachTwoByteString(dupeName, + [&] (std::string* name) { data->set_allocated_functiondisplayname(name); }, + [&] (uint64_t ref) { data->set_functiondisplaynameref(ref); })) + { + return nullptr; + } + } + + auto parent = frame.parent(); + if (parent && depth < HeapSnapshot::MAX_STACK_DEPTH) { + auto protobufParent = getProtobufStackFrame(parent, depth + 1); + if (!protobufParent) + return nullptr; + data->set_allocated_parent(protobufParent); + } + + protobufStackFrame->set_allocated_data(data.release()); + + if (!framesAlreadySerialized.put(id)) + return nullptr; + + return protobufStackFrame.release(); + } + +public: + StreamWriter(JSContext* cx, + ::google::protobuf::io::ZeroCopyOutputStream& stream, + bool wantNames, + JS::CompartmentSet* compartments) + : cx(cx) + , wantNames(wantNames) + , framesAlreadySerialized(cx) + , twoByteStringsAlreadySerialized(cx) + , oneByteStringsAlreadySerialized(cx) + , stream(stream) + , compartments(compartments) + { } + + bool init() { + return framesAlreadySerialized.init() && + twoByteStringsAlreadySerialized.init() && + oneByteStringsAlreadySerialized.init(); + } + + ~StreamWriter() override { } + + virtual bool writeMetadata(uint64_t timestamp) final { + protobuf::Metadata metadata; + metadata.set_timestamp(timestamp); + return writeMessage(metadata); + } + + virtual bool writeNode(const JS::ubi::Node& ubiNode, + EdgePolicy includeEdges) override final { + // NB: de-duplicated string properties must be written in the same order + // here as they are read in `HeapSnapshot::saveNode` or else indices in + // references to already serialized strings will be off. + + protobuf::Node protobufNode; + protobufNode.set_id(ubiNode.identifier()); + + protobufNode.set_coarsetype(JS::ubi::CoarseTypeToUint32(ubiNode.coarseType())); + + auto typeName = TwoByteString(ubiNode.typeName()); + if (NS_WARN_IF(!attachTwoByteString(typeName, + [&] (std::string* name) { protobufNode.set_allocated_typename_(name); }, + [&] (uint64_t ref) { protobufNode.set_typenameref(ref); }))) + { + return false; + } + + mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(cx); + MOZ_ASSERT(mallocSizeOf); + protobufNode.set_size(ubiNode.size(mallocSizeOf)); + + if (includeEdges) { + auto edges = ubiNode.edges(cx, wantNames); + if (NS_WARN_IF(!edges)) + return false; + + for ( ; !edges->empty(); edges->popFront()) { + ubi::Edge& ubiEdge = edges->front(); + if (!ShouldIncludeEdge(compartments, ubiNode, ubiEdge)) { + continue; + } + + protobuf::Edge* protobufEdge = protobufNode.add_edges(); + if (NS_WARN_IF(!protobufEdge)) { + return false; + } + + protobufEdge->set_referent(ubiEdge.referent.identifier()); + + if (wantNames && ubiEdge.name) { + TwoByteString edgeName(Move(ubiEdge.name)); + if (NS_WARN_IF(!attachTwoByteString(edgeName, + [&] (std::string* name) { protobufEdge->set_allocated_name(name); }, + [&] (uint64_t ref) { protobufEdge->set_nameref(ref); }))) + { + return false; + } + } + } + } + + if (ubiNode.hasAllocationStack()) { + auto ubiStackFrame = ubiNode.allocationStack(); + auto protoStackFrame = getProtobufStackFrame(ubiStackFrame); + if (NS_WARN_IF(!protoStackFrame)) + return false; + protobufNode.set_allocated_allocationstack(protoStackFrame); + } + + if (auto className = ubiNode.jsObjectClassName()) { + if (NS_WARN_IF(!attachOneByteString(className, + [&] (std::string* name) { protobufNode.set_allocated_jsobjectclassname(name); }, + [&] (uint64_t ref) { protobufNode.set_jsobjectclassnameref(ref); }))) + { + return false; + } + } + + if (auto scriptFilename = ubiNode.scriptFilename()) { + if (NS_WARN_IF(!attachOneByteString(scriptFilename, + [&] (std::string* name) { protobufNode.set_allocated_scriptfilename(name); }, + [&] (uint64_t ref) { protobufNode.set_scriptfilenameref(ref); }))) + { + return false; + } + } + + return writeMessage(protobufNode); + } +}; + +// A JS::ubi::BreadthFirst handler that serializes a snapshot of the heap into a +// core dump. +class MOZ_STACK_CLASS HeapSnapshotHandler +{ + CoreDumpWriter& writer; + JS::CompartmentSet* compartments; + +public: + HeapSnapshotHandler(CoreDumpWriter& writer, + JS::CompartmentSet* compartments) + : writer(writer), + compartments(compartments) + { } + + // JS::ubi::BreadthFirst handler interface. + + class NodeData { }; + typedef JS::ubi::BreadthFirst<HeapSnapshotHandler> Traversal; + bool operator() (Traversal& traversal, + JS::ubi::Node origin, + const JS::ubi::Edge& edge, + NodeData*, + bool first) + { + // We're only interested in the first time we reach edge.referent, not in + // every edge arriving at that node. "But, don't we want to serialize every + // edge in the heap graph?" you ask. Don't worry! This edge is still + // serialized into the core dump. Serializing a node also serializes each of + // its edges, and if we are traversing a given edge, we must have already + // visited and serialized the origin node and its edges. + if (!first) + return true; + + CoreDumpWriter::EdgePolicy policy; + if (!ShouldIncludeEdge(compartments, origin, edge, &policy)) + return true; + + if (policy == CoreDumpWriter::EXCLUDE_EDGES) + traversal.abandonReferent(); + + return writer.writeNode(edge.referent, policy); + } +}; + + +bool +WriteHeapGraph(JSContext* cx, + const JS::ubi::Node& node, + CoreDumpWriter& writer, + bool wantNames, + JS::CompartmentSet* compartments, + JS::AutoCheckCannotGC& noGC) +{ + // Serialize the starting node to the core dump. + + if (NS_WARN_IF(!writer.writeNode(node, CoreDumpWriter::INCLUDE_EDGES))) { + return false; + } + + // Walk the heap graph starting from the given node and serialize it into the + // core dump. + + HeapSnapshotHandler handler(writer, compartments); + HeapSnapshotHandler::Traversal traversal(cx, handler, noGC); + if (!traversal.init()) + return false; + traversal.wantNames = wantNames; + + bool ok = traversal.addStartVisited(node) && + traversal.traverse(); + + return ok; +} + +static unsigned long +msSinceProcessCreation(const TimeStamp& now) +{ + bool ignored; + auto duration = now - TimeStamp::ProcessCreation(ignored); + return (unsigned long) duration.ToMilliseconds(); +} + +/* static */ already_AddRefed<nsIFile> +HeapSnapshot::CreateUniqueCoreDumpFile(ErrorResult& rv, + const TimeStamp& now, + nsAString& outFilePath) +{ + nsCOMPtr<nsIFile> file; + rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file)); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + auto ms = msSinceProcessCreation(now); + rv = file->AppendNative(nsPrintfCString("%lu.fxsnapshot", ms)); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + rv = file->GetPath(outFilePath); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + return file.forget(); +} + +// Deletion policy for cleaning up PHeapSnapshotTempFileHelperChild pointers. +class DeleteHeapSnapshotTempFileHelperChild +{ +public: + constexpr DeleteHeapSnapshotTempFileHelperChild() { } + + void operator()(PHeapSnapshotTempFileHelperChild* ptr) const { + Unused << NS_WARN_IF(!HeapSnapshotTempFileHelperChild::Send__delete__(ptr)); + } +}; + +// A UniquePtr alias to automatically manage PHeapSnapshotTempFileHelperChild +// pointers. +using UniqueHeapSnapshotTempFileHelperChild = UniquePtr<PHeapSnapshotTempFileHelperChild, + DeleteHeapSnapshotTempFileHelperChild>; + +// Get an nsIOutputStream that we can write the heap snapshot to. In non-e10s +// and in the e10s parent process, open a file directly and create an output +// stream for it. In e10s child processes, we are sandboxed without access to +// the filesystem. Use IPDL to request a file descriptor from the parent +// process. +static already_AddRefed<nsIOutputStream> +getCoreDumpOutputStream(ErrorResult& rv, TimeStamp& start, nsAString& outFilePath) +{ + if (XRE_IsParentProcess()) { + // Create the file and open the output stream directly. + + nsCOMPtr<nsIFile> file = HeapSnapshot::CreateUniqueCoreDumpFile(rv, + start, + outFilePath); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + nsCOMPtr<nsIOutputStream> outputStream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file, + PR_WRONLY, -1, 0); + if (NS_WARN_IF(rv.Failed())) + return nullptr; + + return outputStream.forget(); + } else { + // Request a file descriptor from the parent process over IPDL. + + auto cc = ContentChild::GetSingleton(); + if (!cc) { + rv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + UniqueHeapSnapshotTempFileHelperChild helper( + cc->SendPHeapSnapshotTempFileHelperConstructor()); + if (NS_WARN_IF(!helper)) { + rv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + OpenHeapSnapshotTempFileResponse response; + if (!helper->SendOpenHeapSnapshotTempFile(&response)) { + rv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + if (response.type() == OpenHeapSnapshotTempFileResponse::Tnsresult) { + rv.Throw(response.get_nsresult()); + return nullptr; + } + + auto opened = response.get_OpenedFile(); + outFilePath = opened.path(); + nsCOMPtr<nsIOutputStream> outputStream = + FileDescriptorOutputStream::Create(opened.descriptor()); + if (NS_WARN_IF(!outputStream)) { + rv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + return outputStream.forget(); + } +} + +} // namespace devtools + +namespace dom { + +using namespace JS; +using namespace devtools; + +/* static */ void +ThreadSafeChromeUtils::SaveHeapSnapshot(GlobalObject& global, + const HeapSnapshotBoundaries& boundaries, + nsAString& outFilePath, + ErrorResult& rv) +{ + auto start = TimeStamp::Now(); + + bool wantNames = true; + CompartmentSet compartments; + + nsCOMPtr<nsIOutputStream> outputStream = getCoreDumpOutputStream(rv, start, outFilePath); + if (NS_WARN_IF(rv.Failed())) + return; + + ZeroCopyNSIOutputStream zeroCopyStream(outputStream); + ::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream); + + JSContext* cx = global.Context(); + + { + Maybe<AutoCheckCannotGC> maybeNoGC; + ubi::RootList rootList(cx, maybeNoGC, wantNames); + if (!EstablishBoundaries(cx, rv, boundaries, rootList, compartments)) + return; + + StreamWriter writer(cx, gzipStream, wantNames, + compartments.initialized() ? &compartments : nullptr); + if (NS_WARN_IF(!writer.init())) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + MOZ_ASSERT(maybeNoGC.isSome()); + ubi::Node roots(&rootList); + + // Serialize the initial heap snapshot metadata to the core dump. + if (!writer.writeMetadata(PR_Now()) || + // Serialize the heap graph to the core dump, starting from our list of + // roots. + !WriteHeapGraph(cx, + roots, + writer, + wantNames, + compartments.initialized() ? &compartments : nullptr, + maybeNoGC.ref())) + { + rv.Throw(zeroCopyStream.failed() + ? zeroCopyStream.result() + : NS_ERROR_UNEXPECTED); + return; + } + } +} + +/* static */ already_AddRefed<HeapSnapshot> +ThreadSafeChromeUtils::ReadHeapSnapshot(GlobalObject& global, + const nsAString& filePath, + ErrorResult& rv) +{ + UniquePtr<char[]> path(ToNewCString(filePath)); + if (!path) { + rv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + + AutoMemMap mm; + rv = mm.init(path.get()); + if (rv.Failed()) + return nullptr; + + RefPtr<HeapSnapshot> snapshot = HeapSnapshot::Create( + global.Context(), global, reinterpret_cast<const uint8_t*>(mm.address()), + mm.size(), rv); + + return snapshot.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/heapsnapshot/HeapSnapshot.h b/dom/heapsnapshot/HeapSnapshot.h new file mode 100644 index 000000000..12dfa4c2b --- /dev/null +++ b/dom/heapsnapshot/HeapSnapshot.h @@ -0,0 +1,224 @@ +/* -*- 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_devtools_HeapSnapshot__ +#define mozilla_devtools_HeapSnapshot__ + +#include "js/HashTable.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/devtools/DeserializedNode.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/Maybe.h" +#include "mozilla/RefCounted.h" +#include "mozilla/RefPtr.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" + +#include "CoreDump.pb.h" +#include "nsCOMPtr.h" +#include "nsCRTGlue.h" +#include "nsCycleCollectionParticipant.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" +#include "nsXPCOM.h" + +namespace mozilla { +namespace devtools { + +class DominatorTree; + +struct NSFreePolicy { + void operator()(void* ptr) { + NS_Free(ptr); + } +}; + +using UniqueTwoByteString = UniquePtr<char16_t[], NSFreePolicy>; +using UniqueOneByteString = UniquePtr<char[], NSFreePolicy>; + +class HeapSnapshot final : public nsISupports + , public nsWrapperCache +{ + friend struct DeserializedNode; + friend struct DeserializedEdge; + friend struct DeserializedStackFrame; + friend class JS::ubi::Concrete<JS::ubi::DeserializedNode>; + + explicit HeapSnapshot(JSContext* cx, nsISupports* aParent) + : timestamp(Nothing()) + , rootId(0) + , nodes(cx) + , frames(cx) + , mParent(aParent) + { + MOZ_ASSERT(aParent); + }; + + // Initialize this HeapSnapshot from the given buffer that contains a + // serialized core dump. Do NOT take ownership of the buffer, only borrow it + // for the duration of the call. Return false on failure. + bool init(JSContext* cx, const uint8_t* buffer, uint32_t size); + + using NodeIdSet = js::HashSet<NodeId>; + + // Save the given `protobuf::Node` message in this `HeapSnapshot` as a + // `DeserializedNode`. + bool saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents); + + // Save the given `protobuf::StackFrame` message in this `HeapSnapshot` as a + // `DeserializedStackFrame`. The saved stack frame's id is returned via the + // out parameter. + bool saveStackFrame(const protobuf::StackFrame& frame, + StackFrameId& outFrameId); + +public: + // The maximum number of stack frames that we will serialize into a core + // dump. This helps prevent over-recursion in the protobuf library when + // deserializing stacks. + static const size_t MAX_STACK_DEPTH = 60; + +private: + // If present, a timestamp in the same units that `PR_Now` gives. + Maybe<uint64_t> timestamp; + + // The id of the root node for this deserialized heap graph. + NodeId rootId; + + // The set of nodes in this deserialized heap graph, keyed by id. + using NodeSet = js::HashSet<DeserializedNode, DeserializedNode::HashPolicy>; + NodeSet nodes; + + // The set of stack frames in this deserialized heap graph, keyed by id. + using FrameSet = js::HashSet<DeserializedStackFrame, + DeserializedStackFrame::HashPolicy>; + FrameSet frames; + + Vector<UniqueTwoByteString> internedTwoByteStrings; + Vector<UniqueOneByteString> internedOneByteStrings; + + using StringOrRef = Variant<const std::string*, uint64_t>; + + template<typename CharT, + typename InternedStringSet> + const CharT* getOrInternString(InternedStringSet& internedStrings, + Maybe<StringOrRef>& maybeStrOrRef); + +protected: + nsCOMPtr<nsISupports> mParent; + + virtual ~HeapSnapshot() { } + +public: + // Create a `HeapSnapshot` from the given buffer that contains a serialized + // core dump. Do NOT take ownership of the buffer, only borrow it for the + // duration of the call. + static already_AddRefed<HeapSnapshot> Create(JSContext* cx, + dom::GlobalObject& global, + const uint8_t* buffer, + uint32_t size, + ErrorResult& rv); + + // Creates the `$TEMP_DIR/XXXXXX-XXX.fxsnapshot` core dump file that heap + // snapshots are serialized into. + static already_AddRefed<nsIFile> CreateUniqueCoreDumpFile(ErrorResult& rv, + const TimeStamp& now, + nsAString& outFilePath); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HeapSnapshot) + MOZ_DECLARE_REFCOUNTED_TYPENAME(HeapSnapshot) + + nsISupports* GetParentObject() const { return mParent; } + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + + const char16_t* borrowUniqueString(const char16_t* duplicateString, + size_t length); + + // Get the root node of this heap snapshot's graph. + JS::ubi::Node getRoot() { + MOZ_ASSERT(nodes.initialized()); + auto p = nodes.lookup(rootId); + MOZ_ASSERT(p); + const DeserializedNode& node = *p; + return JS::ubi::Node(const_cast<DeserializedNode*>(&node)); + } + + Maybe<JS::ubi::Node> getNodeById(JS::ubi::Node::Id nodeId) { + auto p = nodes.lookup(nodeId); + if (!p) + return Nothing(); + return Some(JS::ubi::Node(const_cast<DeserializedNode*>(&*p))); + } + + void TakeCensus(JSContext* cx, JS::HandleObject options, + JS::MutableHandleValue rval, ErrorResult& rv); + + void DescribeNode(JSContext* cx, JS::HandleObject breakdown, uint64_t nodeId, + JS::MutableHandleValue rval, ErrorResult& rv); + + already_AddRefed<DominatorTree> ComputeDominatorTree(ErrorResult& rv); + + void ComputeShortestPaths(JSContext*cx, uint64_t start, + const dom::Sequence<uint64_t>& targets, + uint64_t maxNumPaths, + JS::MutableHandleObject results, + ErrorResult& rv); + + dom::Nullable<uint64_t> GetCreationTime() { + static const uint64_t maxTime = uint64_t(1) << 53; + if (timestamp.isSome() && timestamp.ref() <= maxTime) { + return dom::Nullable<uint64_t>(timestamp.ref()); + } + + return dom::Nullable<uint64_t>(); + } +}; + +// A `CoreDumpWriter` is given the data we wish to save in a core dump and +// serializes it to disk, or memory, or a socket, etc. +class CoreDumpWriter +{ +public: + virtual ~CoreDumpWriter() { }; + + // Write the given bits of metadata we would like to associate with this core + // dump. + virtual bool writeMetadata(uint64_t timestamp) = 0; + + enum EdgePolicy : bool { + INCLUDE_EDGES = true, + EXCLUDE_EDGES = false + }; + + // Write the given `JS::ubi::Node` to the core dump. The given `EdgePolicy` + // dictates whether its outgoing edges should also be written to the core + // dump, or excluded. + virtual bool writeNode(const JS::ubi::Node& node, + EdgePolicy includeEdges) = 0; +}; + +// Serialize the heap graph as seen from `node` with the given `CoreDumpWriter`. +// If `wantNames` is true, capture edge names. If `zones` is non-null, only +// capture the sub-graph within the zone set, otherwise capture the whole heap +// graph. Returns false on failure. +bool +WriteHeapGraph(JSContext* cx, + const JS::ubi::Node& node, + CoreDumpWriter& writer, + bool wantNames, + JS::CompartmentSet* compartments, + JS::AutoCheckCannotGC& noGC); + +// Get the mozilla::MallocSizeOf for the current thread's JSRuntime. +MallocSizeOf GetCurrentThreadDebuggerMallocSizeOf(); + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_HeapSnapshot__ diff --git a/dom/heapsnapshot/HeapSnapshotTempFileHelperChild.h b/dom/heapsnapshot/HeapSnapshotTempFileHelperChild.h new file mode 100644 index 000000000..a1d433a5e --- /dev/null +++ b/dom/heapsnapshot/HeapSnapshotTempFileHelperChild.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et 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_devtools_HeapSnapshotTempFileHelperChild_h +#define mozilla_devtools_HeapSnapshotTempFileHelperChild_h + +#include "mozilla/devtools/PHeapSnapshotTempFileHelperChild.h" + +namespace mozilla { +namespace devtools { + +class HeapSnapshotTempFileHelperChild : public PHeapSnapshotTempFileHelperChild +{ + explicit HeapSnapshotTempFileHelperChild() { } + +public: + static inline PHeapSnapshotTempFileHelperChild* Create(); +}; + +/* static */ inline PHeapSnapshotTempFileHelperChild* +HeapSnapshotTempFileHelperChild::Create() +{ + return new HeapSnapshotTempFileHelperChild(); +} + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_HeapSnapshotTempFileHelperChild_h diff --git a/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.cpp b/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.cpp new file mode 100644 index 000000000..7246a9daa --- /dev/null +++ b/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et 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 "mozilla/devtools/HeapSnapshot.h" +#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" +#include "mozilla/ErrorResult.h" +#include "private/pprio.h" + +#include "nsIFile.h" + +namespace mozilla { +namespace devtools { + +using ipc::FileDescriptor; + +static bool +openFileFailure(ErrorResult& rv, + OpenHeapSnapshotTempFileResponse* outResponse) +{ + *outResponse = rv.StealNSResult(); + return true; +} + +bool +HeapSnapshotTempFileHelperParent::RecvOpenHeapSnapshotTempFile( + OpenHeapSnapshotTempFileResponse* outResponse) +{ + auto start = TimeStamp::Now(); + ErrorResult rv; + nsAutoString filePath; + nsCOMPtr<nsIFile> file = HeapSnapshot::CreateUniqueCoreDumpFile(rv, + start, + filePath); + if (NS_WARN_IF(rv.Failed())) + return openFileFailure(rv, outResponse); + + PRFileDesc* prfd; + rv = file->OpenNSPRFileDesc(PR_WRONLY, 0, &prfd); + if (NS_WARN_IF(rv.Failed())) + return openFileFailure(rv, outResponse); + + FileDescriptor::PlatformHandleType handle = + FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd)); + FileDescriptor fd(handle); + *outResponse = OpenedFile(filePath, fd); + return true; +} + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.h b/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.h new file mode 100644 index 000000000..1582279da --- /dev/null +++ b/dom/heapsnapshot/HeapSnapshotTempFileHelperParent.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et 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_devtools_HeapSnapshotTempFileHelperParent_h +#define mozilla_devtools_HeapSnapshotTempFileHelperParent_h + +#include "mozilla/devtools/PHeapSnapshotTempFileHelperParent.h" + +namespace mozilla { +namespace devtools { + +class HeapSnapshotTempFileHelperParent : public PHeapSnapshotTempFileHelperParent +{ + explicit HeapSnapshotTempFileHelperParent() { } + void ActorDestroy(ActorDestroyReason why) override { } + bool RecvOpenHeapSnapshotTempFile(OpenHeapSnapshotTempFileResponse* outResponse) + override; + + public: + static inline PHeapSnapshotTempFileHelperParent* Create(); +}; + +/* static */ inline PHeapSnapshotTempFileHelperParent* +HeapSnapshotTempFileHelperParent::Create() +{ + return new HeapSnapshotTempFileHelperParent(); +} + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_HeapSnapshotTempFileHelperParent_h diff --git a/dom/heapsnapshot/PHeapSnapshotTempFileHelper.ipdl b/dom/heapsnapshot/PHeapSnapshotTempFileHelper.ipdl new file mode 100644 index 000000000..2576470e2 --- /dev/null +++ b/dom/heapsnapshot/PHeapSnapshotTempFileHelper.ipdl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 protocol PContent; + +namespace mozilla { +namespace devtools { + +struct OpenedFile +{ + nsString path; + FileDescriptor descriptor; +}; + +union OpenHeapSnapshotTempFileResponse +{ + nsresult; + OpenedFile; +}; + +sync protocol PHeapSnapshotTempFileHelper +{ + manager PContent; + +parent: + sync OpenHeapSnapshotTempFile() returns (OpenHeapSnapshotTempFileResponse response); + + async __delete__(); +}; + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/ZeroCopyNSIOutputStream.cpp b/dom/heapsnapshot/ZeroCopyNSIOutputStream.cpp new file mode 100644 index 000000000..0c29db7f9 --- /dev/null +++ b/dom/heapsnapshot/ZeroCopyNSIOutputStream.cpp @@ -0,0 +1,100 @@ +/* -*- 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 "mozilla/devtools/ZeroCopyNSIOutputStream.h" + +#include "mozilla/DebugOnly.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace devtools { + +ZeroCopyNSIOutputStream::ZeroCopyNSIOutputStream(nsCOMPtr<nsIOutputStream>& out) + : out(out) + , result_(NS_OK) + , amountUsed(0) + , writtenCount(0) +{ + DebugOnly<bool> nonBlocking = false; + MOZ_ASSERT(out->IsNonBlocking(&nonBlocking) == NS_OK); + MOZ_ASSERT(!nonBlocking); +} + +ZeroCopyNSIOutputStream::~ZeroCopyNSIOutputStream() +{ + if (!failed()) + Unused << NS_WARN_IF(NS_FAILED(writeBuffer())); +} + +nsresult +ZeroCopyNSIOutputStream::writeBuffer() +{ + if (failed()) + return result_; + + if (amountUsed == 0) + return NS_OK; + + int32_t amountWritten = 0; + while (amountWritten < amountUsed) { + uint32_t justWritten = 0; + + result_ = out->Write(buffer + amountWritten, + amountUsed - amountWritten, + &justWritten); + if (NS_WARN_IF(NS_FAILED(result_))) + return result_; + + amountWritten += justWritten; + } + + writtenCount += amountUsed; + amountUsed = 0; + return NS_OK; +} + +// ZeroCopyOutputStream Interface + +bool +ZeroCopyNSIOutputStream::Next(void** data, int* size) +{ + MOZ_ASSERT(data != nullptr); + MOZ_ASSERT(size != nullptr); + + if (failed()) + return false; + + if (amountUsed == BUFFER_SIZE) { + if (NS_FAILED(writeBuffer())) + return false; + } + + *data = buffer + amountUsed; + *size = BUFFER_SIZE - amountUsed; + amountUsed = BUFFER_SIZE; + return true; +} + +void +ZeroCopyNSIOutputStream::BackUp(int count) +{ + MOZ_ASSERT(count >= 0, + "Cannot back up a negative amount of bytes."); + MOZ_ASSERT(amountUsed == BUFFER_SIZE, + "Can only call BackUp directly after calling Next."); + MOZ_ASSERT(count <= amountUsed, + "Can't back up further than we've given out."); + + amountUsed -= count; +} + +::google::protobuf::int64 +ZeroCopyNSIOutputStream::ByteCount() const +{ + return writtenCount + amountUsed; +} + +} // namespace devtools +} // namespace mozilla diff --git a/dom/heapsnapshot/ZeroCopyNSIOutputStream.h b/dom/heapsnapshot/ZeroCopyNSIOutputStream.h new file mode 100644 index 000000000..117fc0f87 --- /dev/null +++ b/dom/heapsnapshot/ZeroCopyNSIOutputStream.h @@ -0,0 +1,70 @@ +/* -*- 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_devtools_ZeroCopyNSIOutputStream__ +#define mozilla_devtools_ZeroCopyNSIOutputStream__ + +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/stubs/common.h> + +#include "nsCOMPtr.h" +#include "nsIOutputStream.h" + +namespace mozilla { +namespace devtools { + +// A `google::protobuf::io::ZeroCopyOutputStream` implementation that uses an +// `nsIOutputStream` under the covers. +// +// This class will automatically write and flush its data to the +// `nsIOutputStream` in its destructor, but if you care whether that call +// succeeds or fails, then you should call the `flush` method yourself. Errors +// will be logged, however. +class MOZ_STACK_CLASS ZeroCopyNSIOutputStream + : public ::google::protobuf::io::ZeroCopyOutputStream +{ + static const int BUFFER_SIZE = 8192; + + // The nsIOutputStream we are streaming to. + nsCOMPtr<nsIOutputStream>& out; + + // The buffer we write data to before passing it to the output stream. + char buffer[BUFFER_SIZE]; + + // The status of writing to the underlying output stream. + nsresult result_; + + // The number of bytes in the buffer that have been used thus far. + int amountUsed; + + // Excluding the amount of the buffer currently used (which hasn't been + // written and flushed yet), this is the number of bytes written to the output + // stream. + int64_t writtenCount; + + // Write the internal buffer to the output stream and flush it. + nsresult writeBuffer(); + +public: + explicit ZeroCopyNSIOutputStream(nsCOMPtr<nsIOutputStream>& out); + + nsresult flush() { return writeBuffer(); } + + // Return true if writing to the underlying output stream ever failed. + bool failed() const { return NS_FAILED(result_); } + + nsresult result() const { return result_; } + + // ZeroCopyOutputStream Interface + virtual ~ZeroCopyNSIOutputStream() override; + virtual bool Next(void** data, int* size) override; + virtual void BackUp(int count) override; + virtual ::google::protobuf::int64 ByteCount() const override; +}; + +} // namespace devtools +} // namespace mozilla + +#endif // mozilla_devtools_ZeroCopyNSIOutputStream__ diff --git a/dom/heapsnapshot/generate-core-dump-sources.sh b/dom/heapsnapshot/generate-core-dump-sources.sh new file mode 100644 index 000000000..97e492ff0 --- /dev/null +++ b/dom/heapsnapshot/generate-core-dump-sources.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# A script to generate devtools/server/CoreDump.pb.{h,cc} from +# devtools/server/CoreDump.proto. This script assumes you have +# downloaded and installed the protocol buffer compiler, and that it is either +# on your $PATH or located at $PROTOC_PATH. +# +# These files were last compiled with libprotoc 2.4.1. + +set -e + +cd $(dirname $0) + +if [ -n $PROTOC_PATH ]; then + PROTOC_PATH=`which protoc` +fi + +if [ ! -e $PROTOC_PATH ]; then + echo You must install the protocol compiler from + echo https://code.google.com/p/protobuf/downloads/list + exit 1 +fi + +echo Using $PROTOC_PATH as the protocol compiler + +$PROTOC_PATH --cpp_out="." CoreDump.proto diff --git a/dom/heapsnapshot/moz.build b/dom/heapsnapshot/moz.build new file mode 100644 index 000000000..3fb6b0552 --- /dev/null +++ b/dom/heapsnapshot/moz.build @@ -0,0 +1,52 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files('**'): + BUG_COMPONENT = ('Firefox', 'Developer Tools: Memory') + +if CONFIG['ENABLE_TESTS']: + DIRS += ['tests/gtest'] + +XPCSHELL_TESTS_MANIFESTS += [ 'tests/unit/xpcshell.ini' ] +MOCHITEST_MANIFESTS += [ 'tests/mochitest/mochitest.ini' ] +MOCHITEST_CHROME_MANIFESTS += [ 'tests/mochitest/chrome.ini' ] + +EXPORTS.mozilla.devtools += [ + 'AutoMemMap.h', + 'CoreDump.pb.h', + 'DeserializedNode.h', + 'DominatorTree.h', + 'FileDescriptorOutputStream.h', + 'HeapSnapshot.h', + 'HeapSnapshotTempFileHelperChild.h', + 'HeapSnapshotTempFileHelperParent.h', + 'ZeroCopyNSIOutputStream.h', +] + +IPDL_SOURCES += [ + 'PHeapSnapshotTempFileHelper.ipdl', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +SOURCES += [ + 'AutoMemMap.cpp', + 'CoreDump.pb.cc', + 'DeserializedNode.cpp', + 'DominatorTree.cpp', + 'FileDescriptorOutputStream.cpp', + 'HeapSnapshot.cpp', + 'HeapSnapshotTempFileHelperParent.cpp', + 'ZeroCopyNSIOutputStream.cpp', +] + +# Disable RTTI in google protocol buffer +DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/dom/heapsnapshot/tests/gtest/DeserializedNodeUbiNodes.cpp b/dom/heapsnapshot/tests/gtest/DeserializedNodeUbiNodes.cpp new file mode 100644 index 000000000..e236a0acf --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/DeserializedNodeUbiNodes.cpp @@ -0,0 +1,100 @@ +/* -*- 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/. */ + +// Test that the `JS::ubi::Node`s we create from +// `mozilla::devtools::DeserializedNode` instances look and behave as we would +// like. + +#include "DevTools.h" +#include "js/TypeDecls.h" +#include "mozilla/devtools/DeserializedNode.h" + +using testing::Field; +using testing::ReturnRef; + +// A mock DeserializedNode for testing. +struct MockDeserializedNode : public DeserializedNode +{ + MockDeserializedNode(NodeId id, const char16_t* typeName, uint64_t size) + : DeserializedNode(id, typeName, size) + { } + + bool addEdge(DeserializedEdge&& edge) + { + return edges.append(Move(edge)); + } + + MOCK_METHOD1(getEdgeReferent, JS::ubi::Node(const DeserializedEdge&)); +}; + +size_t fakeMallocSizeOf(const void*) { + EXPECT_TRUE(false); + MOZ_ASSERT_UNREACHABLE("fakeMallocSizeOf should never be called because " + "DeserializedNodes report the deserialized size."); + return 0; +} + +DEF_TEST(DeserializedNodeUbiNodes, { + const char16_t* typeName = u"TestTypeName"; + const char* className = "MyObjectClassName"; + const char* filename = "my-cool-filename.js"; + + NodeId id = uint64_t(1) << 33; + uint64_t size = uint64_t(1) << 60; + MockDeserializedNode mocked(id, typeName, size); + mocked.coarseType = JS::ubi::CoarseType::Script; + mocked.jsObjectClassName = className; + mocked.scriptFilename = filename; + + DeserializedNode& deserialized = mocked; + JS::ubi::Node ubi(&deserialized); + + // Test the ubi::Node accessors. + + EXPECT_EQ(size, ubi.size(fakeMallocSizeOf)); + EXPECT_EQ(typeName, ubi.typeName()); + EXPECT_EQ(JS::ubi::CoarseType::Script, ubi.coarseType()); + EXPECT_EQ(id, ubi.identifier()); + EXPECT_FALSE(ubi.isLive()); + EXPECT_EQ(ubi.jsObjectClassName(), className); + EXPECT_EQ(ubi.scriptFilename(), filename); + + // Test the ubi::Node's edges. + + UniquePtr<DeserializedNode> referent1(new MockDeserializedNode(1, + nullptr, + 10)); + DeserializedEdge edge1(referent1->id); + mocked.addEdge(Move(edge1)); + EXPECT_CALL(mocked, getEdgeReferent(EdgeTo(referent1->id))) + .Times(1) + .WillOnce(Return(JS::ubi::Node(referent1.get()))); + + UniquePtr<DeserializedNode> referent2(new MockDeserializedNode(2, + nullptr, + 20)); + DeserializedEdge edge2(referent2->id); + mocked.addEdge(Move(edge2)); + EXPECT_CALL(mocked, getEdgeReferent(EdgeTo(referent2->id))) + .Times(1) + .WillOnce(Return(JS::ubi::Node(referent2.get()))); + + UniquePtr<DeserializedNode> referent3(new MockDeserializedNode(3, + nullptr, + 30)); + DeserializedEdge edge3(referent3->id); + mocked.addEdge(Move(edge3)); + EXPECT_CALL(mocked, getEdgeReferent(EdgeTo(referent3->id))) + .Times(1) + .WillOnce(Return(JS::ubi::Node(referent3.get()))); + + auto range = ubi.edges(cx); + ASSERT_TRUE(!!range); + + for ( ; !range->empty(); range->popFront()) { + // Nothing to do here. This loop ensures that we get each edge referent + // that we expect above. + } + }); diff --git a/dom/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp b/dom/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp new file mode 100644 index 000000000..72e363934 --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/DeserializedStackFrameUbiStackFrames.cpp @@ -0,0 +1,91 @@ +/* -*- 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/. */ + +// Test that the `JS::ubi::StackFrame`s we create from +// `mozilla::devtools::DeserializedStackFrame` instances look and behave as we would +// like. + +#include "DevTools.h" +#include "js/TypeDecls.h" +#include "mozilla/devtools/DeserializedNode.h" + +using testing::Field; +using testing::ReturnRef; + +// A mock DeserializedStackFrame for testing. +struct MockDeserializedStackFrame : public DeserializedStackFrame +{ + MockDeserializedStackFrame() : DeserializedStackFrame() { } +}; + +DEF_TEST(DeserializedStackFrameUbiStackFrames, { + StackFrameId id = uint64_t(1) << 42; + uint32_t line = 1337; + uint32_t column = 9; // 3 space tabs!? + const char16_t* source = u"my-javascript-file.js"; + const char16_t* functionDisplayName = u"myFunctionName"; + + MockDeserializedStackFrame mocked; + mocked.id = id; + mocked.line = line; + mocked.column = column; + mocked.source = source; + mocked.functionDisplayName = functionDisplayName; + + DeserializedStackFrame& deserialized = mocked; + JS::ubi::StackFrame ubiFrame(&deserialized); + + // Test the JS::ubi::StackFrame accessors. + + EXPECT_EQ(id, ubiFrame.identifier()); + EXPECT_EQ(JS::ubi::StackFrame(), ubiFrame.parent()); + EXPECT_EQ(line, ubiFrame.line()); + EXPECT_EQ(column, ubiFrame.column()); + EXPECT_EQ(JS::ubi::AtomOrTwoByteChars(source), ubiFrame.source()); + EXPECT_EQ(JS::ubi::AtomOrTwoByteChars(functionDisplayName), + ubiFrame.functionDisplayName()); + EXPECT_FALSE(ubiFrame.isSelfHosted(cx)); + EXPECT_FALSE(ubiFrame.isSystem()); + + JS::RootedObject savedFrame(cx); + EXPECT_TRUE(ubiFrame.constructSavedFrameStack(cx, &savedFrame)); + + uint32_t frameLine; + ASSERT_EQ(JS::SavedFrameResult::Ok, JS::GetSavedFrameLine(cx, savedFrame, &frameLine)); + EXPECT_EQ(line, frameLine); + + uint32_t frameColumn; + ASSERT_EQ(JS::SavedFrameResult::Ok, JS::GetSavedFrameColumn(cx, savedFrame, &frameColumn)); + EXPECT_EQ(column, frameColumn); + + JS::RootedObject parent(cx); + ASSERT_EQ(JS::SavedFrameResult::Ok, JS::GetSavedFrameParent(cx, savedFrame, &parent)); + EXPECT_EQ(nullptr, parent); + + ASSERT_EQ(NS_strlen(source), 21U); + char16_t sourceBuf[21] = {}; + + // Test when the length is shorter than the string length. + auto written = ubiFrame.source(RangedPtr<char16_t>(sourceBuf), 3); + EXPECT_EQ(written, 3U); + for (size_t i = 0; i < 3; i++) { + EXPECT_EQ(sourceBuf[i], source[i]); + } + + written = ubiFrame.source(RangedPtr<char16_t>(sourceBuf), 21); + EXPECT_EQ(written, 21U); + for (size_t i = 0; i < 21; i++) { + EXPECT_EQ(sourceBuf[i], source[i]); + } + + ASSERT_EQ(NS_strlen(functionDisplayName), 14U); + char16_t nameBuf[14] = {}; + + written = ubiFrame.functionDisplayName(RangedPtr<char16_t>(nameBuf), 14); + EXPECT_EQ(written, 14U); + for (size_t i = 0; i < 14; i++) { + EXPECT_EQ(nameBuf[i], functionDisplayName[i]); + } +}); diff --git a/dom/heapsnapshot/tests/gtest/DevTools.h b/dom/heapsnapshot/tests/gtest/DevTools.h new file mode 100644 index 000000000..6eb5cfe21 --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/DevTools.h @@ -0,0 +1,276 @@ +/* -*- 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_devtools_gtest_DevTools__ +#define mozilla_devtools_gtest_DevTools__ + +#include "CoreDump.pb.h" +#include "jsapi.h" +#include "jspubtd.h" +#include "nsCRTGlue.h" + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "mozilla/devtools/HeapSnapshot.h" +#include "mozilla/dom/ChromeUtils.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/Move.h" +#include "js/Principals.h" +#include "js/UbiNode.h" +#include "js/UniquePtr.h" + +using namespace mozilla; +using namespace mozilla::devtools; +using namespace mozilla::dom; +using namespace testing; + +// GTest fixture class that all of our tests derive from. +struct DevTools : public ::testing::Test { + bool _initialized; + JSContext* cx; + JSCompartment* compartment; + JS::Zone* zone; + JS::PersistentRootedObject global; + + DevTools() + : _initialized(false), + cx(nullptr) + { } + + virtual void SetUp() { + MOZ_ASSERT(!_initialized); + + cx = getContext(); + if (!cx) + return; + + JS_BeginRequest(cx); + + global.init(cx, createGlobal()); + if (!global) + return; + JS_EnterCompartment(cx, global); + + compartment = js::GetContextCompartment(cx); + zone = js::GetContextZone(cx); + + _initialized = true; + } + + JSContext* getContext() { + return CycleCollectedJSContext::Get()->Context(); + } + + static void reportError(JSContext* cx, const char* message, JSErrorReport* report) { + fprintf(stderr, "%s:%u:%s\n", + report->filename ? report->filename : "<no filename>", + (unsigned int) report->lineno, + message); + } + + static const JSClass* getGlobalClass() { + static const JSClassOps globalClassOps = { + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, + JS_GlobalObjectTraceHook + }; + static const JSClass globalClass = { + "global", JSCLASS_GLOBAL_FLAGS, + &globalClassOps + }; + return &globalClass; + } + + JSObject* createGlobal() + { + /* Create the global object. */ + JS::RootedObject newGlobal(cx); + JS::CompartmentOptions options; + options.behaviors().setVersion(JSVERSION_LATEST); + newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), nullptr, + JS::FireOnNewGlobalHook, options); + if (!newGlobal) + return nullptr; + + JSAutoCompartment ac(cx, newGlobal); + + /* Populate the global object with the standard globals, like Object and + Array. */ + if (!JS_InitStandardClasses(cx, newGlobal)) + return nullptr; + + return newGlobal; + } + + virtual void TearDown() { + _initialized = false; + + if (global) { + JS_LeaveCompartment(cx, nullptr); + global = nullptr; + } + if (cx) + JS_EndRequest(cx); + } +}; + + +// Helper to define a test and ensure that the fixture is initialized properly. +#define DEF_TEST(name, body) \ + TEST_F(DevTools, name) { \ + ASSERT_TRUE(_initialized); \ + body \ + } + + +// Fake JS::ubi::Node implementation +class MOZ_STACK_CLASS FakeNode +{ +public: + JS::ubi::EdgeVector edges; + JSCompartment* compartment; + JS::Zone* zone; + size_t size; + + explicit FakeNode() + : edges(), + compartment(nullptr), + zone(nullptr), + size(1) + { } +}; + +namespace JS { +namespace ubi { + +template<> +class Concrete<FakeNode> : public Base +{ + const char16_t* typeName() const override { + return concreteTypeName; + } + + js::UniquePtr<EdgeRange> edges(JSContext*, bool) const override { + return js::UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges)); + } + + Size size(mozilla::MallocSizeOf) const override { + return get().size; + } + + JS::Zone* zone() const override { + return get().zone; + } + + JSCompartment* compartment() const override { + return get().compartment; + } + +protected: + explicit Concrete(FakeNode* ptr) : Base(ptr) { } + FakeNode& get() const { return *static_cast<FakeNode*>(ptr); } + +public: + static const char16_t concreteTypeName[]; + static void construct(void* storage, FakeNode* ptr) { + new (storage) Concrete(ptr); + } +}; + +const char16_t Concrete<FakeNode>::concreteTypeName[] = u"FakeNode"; + +} // namespace ubi +} // namespace JS + +void AddEdge(FakeNode& node, FakeNode& referent, const char16_t* edgeName = nullptr) { + char16_t* ownedEdgeName = nullptr; + if (edgeName) { + ownedEdgeName = NS_strdup(edgeName); + ASSERT_NE(ownedEdgeName, nullptr); + } + + JS::ubi::Edge edge(ownedEdgeName, &referent); + ASSERT_TRUE(node.edges.append(mozilla::Move(edge))); +} + + +// Custom GMock Matchers + +// Use the testing namespace to avoid static analysis failures in the gmock +// matcher classes that get generated from MATCHER_P macros. +namespace testing { + +// Ensure that given node has the expected number of edges. +MATCHER_P2(EdgesLength, cx, expectedLength, "") { + auto edges = arg.edges(cx); + if (!edges) + return false; + + int actualLength = 0; + for ( ; !edges->empty(); edges->popFront()) + actualLength++; + + return Matcher<int>(Eq(expectedLength)) + .MatchAndExplain(actualLength, result_listener); +} + +// Get the nth edge and match it with the given matcher. +MATCHER_P3(Edge, cx, n, matcher, "") { + auto edges = arg.edges(cx); + if (!edges) + return false; + + int i = 0; + for ( ; !edges->empty(); edges->popFront()) { + if (i == n) { + return Matcher<const JS::ubi::Edge&>(matcher) + .MatchAndExplain(edges->front(), result_listener); + } + + i++; + } + + return false; +} + +// Ensures that two char16_t* strings are equal. +MATCHER_P(UTF16StrEq, str, "") { + return NS_strcmp(arg, str) == 0; +} + +MATCHER_P(UniqueUTF16StrEq, str, "") { + return NS_strcmp(arg.get(), str) == 0; +} + +MATCHER(UniqueIsNull, "") { + return arg.get() == nullptr; +} + +// Matches an edge whose referent is the node with the given id. +MATCHER_P(EdgeTo, id, "") { + return Matcher<const DeserializedEdge&>(Field(&DeserializedEdge::referent, id)) + .MatchAndExplain(arg, result_listener); +} + +} // namespace testing + + +// A mock `Writer` class to be used with testing `WriteHeapGraph`. +class MockWriter : public CoreDumpWriter +{ +public: + virtual ~MockWriter() override { } + MOCK_METHOD2(writeNode, bool(const JS::ubi::Node&, CoreDumpWriter::EdgePolicy)); + MOCK_METHOD1(writeMetadata, bool(uint64_t)); +}; + +void ExpectWriteNode(MockWriter& writer, FakeNode& node) { + EXPECT_CALL(writer, writeNode(Eq(JS::ubi::Node(&node)), _)) + .Times(1) + .WillOnce(Return(true)); +} + +#endif // mozilla_devtools_gtest_DevTools__ diff --git a/dom/heapsnapshot/tests/gtest/DoesCrossCompartmentBoundaries.cpp b/dom/heapsnapshot/tests/gtest/DoesCrossCompartmentBoundaries.cpp new file mode 100644 index 000000000..bc517d6d9 --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/DoesCrossCompartmentBoundaries.cpp @@ -0,0 +1,73 @@ +/* -*- 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/. */ + +// Test that heap snapshots cross compartment boundaries when expected. + +#include "DevTools.h" + +DEF_TEST(DoesCrossCompartmentBoundaries, { + // Create a new global to get a new compartment. + JS::CompartmentOptions options; + JS::RootedObject newGlobal(cx, JS_NewGlobalObject(cx, + getGlobalClass(), + nullptr, + JS::FireOnNewGlobalHook, + options)); + ASSERT_TRUE(newGlobal); + JSCompartment* newCompartment = nullptr; + { + JSAutoCompartment ac(cx, newGlobal); + ASSERT_TRUE(JS_InitStandardClasses(cx, newGlobal)); + newCompartment = js::GetContextCompartment(cx); + } + ASSERT_TRUE(newCompartment); + ASSERT_NE(newCompartment, compartment); + + // Our set of target compartments is both the old and new compartments. + JS::CompartmentSet targetCompartments; + ASSERT_TRUE(targetCompartments.init()); + ASSERT_TRUE(targetCompartments.put(compartment)); + ASSERT_TRUE(targetCompartments.put(newCompartment)); + + FakeNode nodeA; + FakeNode nodeB; + FakeNode nodeC; + FakeNode nodeD; + + nodeA.compartment = compartment; + nodeB.compartment = nullptr; + nodeC.compartment = newCompartment; + nodeD.compartment = nullptr; + + AddEdge(nodeA, nodeB); + AddEdge(nodeA, nodeC); + AddEdge(nodeB, nodeD); + + ::testing::NiceMock<MockWriter> writer; + + // Should serialize nodeA, because it is in one of our target compartments. + ExpectWriteNode(writer, nodeA); + + // Should serialize nodeB, because it doesn't belong to a compartment and is + // therefore assumed to be shared. + ExpectWriteNode(writer, nodeB); + + // Should also serialize nodeC, which is in our target compartments, but a + // different compartment than A. + ExpectWriteNode(writer, nodeC); + + // Should serialize nodeD because it's reachable via B and both nodes B and D + // don't belong to a specific compartment. + ExpectWriteNode(writer, nodeD); + + JS::AutoCheckCannotGC noGC(cx); + + ASSERT_TRUE(WriteHeapGraph(cx, + JS::ubi::Node(&nodeA), + writer, + /* wantNames = */ false, + &targetCompartments, + noGC)); + }); diff --git a/dom/heapsnapshot/tests/gtest/DoesntCrossCompartmentBoundaries.cpp b/dom/heapsnapshot/tests/gtest/DoesntCrossCompartmentBoundaries.cpp new file mode 100644 index 000000000..2fe5e6ace --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/DoesntCrossCompartmentBoundaries.cpp @@ -0,0 +1,64 @@ +/* -*- 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/. */ + +// Test that heap snapshots walk the compartment boundaries correctly. + +#include "DevTools.h" + +DEF_TEST(DoesntCrossCompartmentBoundaries, { + // Create a new global to get a new compartment. + JS::CompartmentOptions options; + JS::RootedObject newGlobal(cx, JS_NewGlobalObject(cx, + getGlobalClass(), + nullptr, + JS::FireOnNewGlobalHook, + options)); + ASSERT_TRUE(newGlobal); + JSCompartment* newCompartment = nullptr; + { + JSAutoCompartment ac(cx, newGlobal); + ASSERT_TRUE(JS_InitStandardClasses(cx, newGlobal)); + newCompartment = js::GetContextCompartment(cx); + } + ASSERT_TRUE(newCompartment); + ASSERT_NE(newCompartment, compartment); + + // Our set of target compartments is only the pre-existing compartment and + // does not include the new compartment. + JS::CompartmentSet targetCompartments; + ASSERT_TRUE(targetCompartments.init()); + ASSERT_TRUE(targetCompartments.put(compartment)); + + FakeNode nodeA; + FakeNode nodeB; + FakeNode nodeC; + + nodeA.compartment = compartment; + nodeB.compartment = nullptr; + nodeC.compartment = newCompartment; + + AddEdge(nodeA, nodeB); + AddEdge(nodeB, nodeC); + + ::testing::NiceMock<MockWriter> writer; + + // Should serialize nodeA, because it is in our target compartments. + ExpectWriteNode(writer, nodeA); + + // Should serialize nodeB, because it doesn't belong to a compartment and is + // therefore assumed to be shared. + ExpectWriteNode(writer, nodeB); + + // But we shouldn't ever serialize nodeC. + + JS::AutoCheckCannotGC noGC(cx); + + ASSERT_TRUE(WriteHeapGraph(cx, + JS::ubi::Node(&nodeA), + writer, + /* wantNames = */ false, + &targetCompartments, + noGC)); + }); diff --git a/dom/heapsnapshot/tests/gtest/SerializesEdgeNames.cpp b/dom/heapsnapshot/tests/gtest/SerializesEdgeNames.cpp new file mode 100644 index 000000000..be135dbb4 --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/SerializesEdgeNames.cpp @@ -0,0 +1,53 @@ +/* -*- 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/. */ + +// Test that edge names get serialized correctly. + +#include "DevTools.h" + +using testing::Field; +using testing::IsNull; +using testing::Property; +using testing::Return; + +DEF_TEST(SerializesEdgeNames, { + FakeNode node; + FakeNode referent; + + const char16_t edgeName[] = u"edge name"; + const char16_t emptyStr[] = u""; + + AddEdge(node, referent, edgeName); + AddEdge(node, referent, emptyStr); + AddEdge(node, referent, nullptr); + + ::testing::NiceMock<MockWriter> writer; + + // Should get the node with edges once. + EXPECT_CALL( + writer, + writeNode(AllOf(EdgesLength(cx, 3), + Edge(cx, 0, Field(&JS::ubi::Edge::name, + UniqueUTF16StrEq(edgeName))), + Edge(cx, 1, Field(&JS::ubi::Edge::name, + UniqueUTF16StrEq(emptyStr))), + Edge(cx, 2, Field(&JS::ubi::Edge::name, + UniqueIsNull()))), + _) + ) + .Times(1) + .WillOnce(Return(true)); + + // Should get the referent node that doesn't have any edges once. + ExpectWriteNode(writer, referent); + + JS::AutoCheckCannotGC noGC(cx); + ASSERT_TRUE(WriteHeapGraph(cx, + JS::ubi::Node(&node), + writer, + /* wantNames = */ true, + /* zones = */ nullptr, + noGC)); + }); diff --git a/dom/heapsnapshot/tests/gtest/SerializesEverythingInHeapGraphOnce.cpp b/dom/heapsnapshot/tests/gtest/SerializesEverythingInHeapGraphOnce.cpp new file mode 100644 index 000000000..475442df8 --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/SerializesEverythingInHeapGraphOnce.cpp @@ -0,0 +1,37 @@ +/* -*- 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/. */ + +// Test that everything in the heap graph gets serialized once, and only once. + +#include "DevTools.h" + +DEF_TEST(SerializesEverythingInHeapGraphOnce, { + FakeNode nodeA; + FakeNode nodeB; + FakeNode nodeC; + FakeNode nodeD; + + AddEdge(nodeA, nodeB); + AddEdge(nodeB, nodeC); + AddEdge(nodeC, nodeD); + AddEdge(nodeD, nodeA); + + ::testing::NiceMock<MockWriter> writer; + + // Should serialize each node once. + ExpectWriteNode(writer, nodeA); + ExpectWriteNode(writer, nodeB); + ExpectWriteNode(writer, nodeC); + ExpectWriteNode(writer, nodeD); + + JS::AutoCheckCannotGC noGC(cx); + + ASSERT_TRUE(WriteHeapGraph(cx, + JS::ubi::Node(&nodeA), + writer, + /* wantNames = */ false, + /* zones = */ nullptr, + noGC)); + }); diff --git a/dom/heapsnapshot/tests/gtest/SerializesTypeNames.cpp b/dom/heapsnapshot/tests/gtest/SerializesTypeNames.cpp new file mode 100644 index 000000000..a259c297b --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/SerializesTypeNames.cpp @@ -0,0 +1,30 @@ +/* -*- 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/. */ + +// Test that a ubi::Node's typeName gets properly serialized into a core dump. + +#include "DevTools.h" + +using testing::Property; +using testing::Return; + +DEF_TEST(SerializesTypeNames, { + FakeNode node; + + ::testing::NiceMock<MockWriter> writer; + EXPECT_CALL(writer, writeNode(Property(&JS::ubi::Node::typeName, + UTF16StrEq(u"FakeNode")), + _)) + .Times(1) + .WillOnce(Return(true)); + + JS::AutoCheckCannotGC noGC(cx); + ASSERT_TRUE(WriteHeapGraph(cx, + JS::ubi::Node(&node), + writer, + /* wantNames = */ true, + /* zones = */ nullptr, + noGC)); + }); diff --git a/dom/heapsnapshot/tests/gtest/moz.build b/dom/heapsnapshot/tests/gtest/moz.build new file mode 100644 index 000000000..08c31e47c --- /dev/null +++ b/dom/heapsnapshot/tests/gtest/moz.build @@ -0,0 +1,31 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Library('devtoolstests') + +LOCAL_INCLUDES += [ + '../..', +] + +UNIFIED_SOURCES = [ + 'DeserializedNodeUbiNodes.cpp', + 'DeserializedStackFrameUbiStackFrames.cpp', + 'DoesCrossCompartmentBoundaries.cpp', + 'DoesntCrossCompartmentBoundaries.cpp', + 'SerializesEdgeNames.cpp', + 'SerializesEverythingInHeapGraphOnce.cpp', + 'SerializesTypeNames.cpp', +] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] + +# THE MOCK_METHOD2 macro from gtest triggers this clang warning and it's hard +# to work around, so we just ignore it. +if CONFIG['CLANG_CXX']: + CXXFLAGS += ['-Wno-inconsistent-missing-override'] + +FINAL_LIBRARY = 'xul-gtest' diff --git a/dom/heapsnapshot/tests/mochitest/chrome.ini b/dom/heapsnapshot/tests/mochitest/chrome.ini new file mode 100644 index 000000000..497b6fe37 --- /dev/null +++ b/dom/heapsnapshot/tests/mochitest/chrome.ini @@ -0,0 +1,8 @@ +[DEFAULT] +tags = devtools devtools-memory +skip-if = os == 'android' +support-files = + +[test_DominatorTree_01.html] +[test_SaveHeapSnapshot.html] + diff --git a/dom/heapsnapshot/tests/mochitest/mochitest.ini b/dom/heapsnapshot/tests/mochitest/mochitest.ini new file mode 100644 index 000000000..5e7aa8d10 --- /dev/null +++ b/dom/heapsnapshot/tests/mochitest/mochitest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +tags = devtools devtools-memory +support-files = + +[test_saveHeapSnapshot_e10s_01.html] + diff --git a/dom/heapsnapshot/tests/mochitest/test_DominatorTree_01.html b/dom/heapsnapshot/tests/mochitest/test_DominatorTree_01.html new file mode 100644 index 000000000..1f9d8c080 --- /dev/null +++ b/dom/heapsnapshot/tests/mochitest/test_DominatorTree_01.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +Sanity test that we can compute dominator trees from a heap snapshot in a web window. +--> +<head> + <meta charset="utf-8"> + <title>ChromeUtils.saveHeapSnapshot test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script> +SimpleTest.waitForExplicitFinish(); +window.onload = function() { + const path = ChromeUtils.saveHeapSnapshot({ runtime: true }); + const snapshot = ChromeUtils.readHeapSnapshot(path); + + const dominatorTree = snapshot.computeDominatorTree(); + ok(dominatorTree); + ok(dominatorTree instanceof DominatorTree); + + let threw = false; + try { + new DominatorTree(); + } catch (e) { + threw = true; + } + ok(threw, "Constructor shouldn't be usable"); + + SimpleTest.finish(); +}; +</script> +</pre> +</body> +</html> diff --git a/dom/heapsnapshot/tests/mochitest/test_SaveHeapSnapshot.html b/dom/heapsnapshot/tests/mochitest/test_SaveHeapSnapshot.html new file mode 100644 index 000000000..f150a99c7 --- /dev/null +++ b/dom/heapsnapshot/tests/mochitest/test_SaveHeapSnapshot.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1024774 - Sanity test that we can take a heap snapshot in a web window. +--> +<head> + <meta charset="utf-8"> + <title>ChromeUtils.saveHeapSnapshot test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script> +SimpleTest.waitForExplicitFinish(); +window.onload = function() { + ok(ChromeUtils, "The ChromeUtils interface should be exposed in chrome windows."); + ChromeUtils.saveHeapSnapshot({ runtime: true }); + ok(true, "Should save a heap snapshot and shouldn't throw."); + SimpleTest.finish(); +}; +</script> +</pre> +</body> +</html> diff --git a/dom/heapsnapshot/tests/mochitest/test_saveHeapSnapshot_e10s_01.html b/dom/heapsnapshot/tests/mochitest/test_saveHeapSnapshot_e10s_01.html new file mode 100644 index 000000000..15f88f8e0 --- /dev/null +++ b/dom/heapsnapshot/tests/mochitest/test_saveHeapSnapshot_e10s_01.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<!-- +Bug 1201597 - Sanity test that we can take a heap snapshot in an e10s child process. +--> +<html> +<head> + <title>saveHeapSnapshot in e10s child processes</title> + <script type="application/javascript" + src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + window.onerror = function (msg, url, line, col, err) { + ok(false, "@" + url + ":" + line + ":" + col + ": " + msg + "\n" + err.stack); + }; + + SimpleTest.waitForExplicitFinish(); + + var childFrameURL = "data:text/html,<!DOCTYPE HTML><html><body></body></html>"; + + // This function is stringified and loaded in the child process as a frame + // script. + function childFrameScript() { + try { + ChromeUtils.saveHeapSnapshot({ runtime: true }); + } catch (err) { + sendAsyncMessage("testSaveHeapSnapshot:error", + { error: err.toString() }); + return; + } + + sendAsyncMessage("testSaveHeapSnapshot:done", {}); + } + + // Kick everything off on load. + window.onload = function () { + info("window.onload fired"); + SpecialPowers.addPermission("browser", true, document); + SpecialPowers.pushPrefEnv({ + "set": [ + ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["browser.pagethumbnails.capturing_disabled", true] + ] + }, function () { + var iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = childFrameURL; + + + iframe.addEventListener("mozbrowserloadend", function onLoadEnd() { + iframe.removeEventListener("mozbrowserloadend", onLoadEnd); + info("iframe done loading"); + + var mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + + function onError(e) { + ok(false, e.data.error); + } + mm.addMessageListener("testSaveHeapSnapshot:error", onError); + + mm.addMessageListener("testSaveHeapSnapshot:done", function onMsg() { + mm.removeMessageListener("testSaveHeapSnapshot:done", onMsg); + mm.removeMessageListener("testSaveHeapSnapshot:error", onError); + ok(true, "Saved heap snapshot in child process"); + SimpleTest.finish(); + }); + + info("Loading frame script to save heap snapshot"); + mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();", + false); + }); + + info("Loading iframe"); + document.body.appendChild(iframe); + }); + }; + </script> +</window> diff --git a/dom/heapsnapshot/tests/unit/.eslintrc.js b/dom/heapsnapshot/tests/unit/.eslintrc.js new file mode 100644 index 000000000..59adf410a --- /dev/null +++ b/dom/heapsnapshot/tests/unit/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the common devtools xpcshell eslintrc config. + "extends": "../../../../.eslintrc.xpcshell.js" +}; diff --git a/dom/heapsnapshot/tests/unit/Census.jsm b/dom/heapsnapshot/tests/unit/Census.jsm new file mode 100644 index 000000000..f8fb1ce44 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/Census.jsm @@ -0,0 +1,165 @@ +// Functions for checking results returned by +// Debugger.Memory.prototype.takeCensus and +// HeapSnapshot.prototype.takeCensus. Adapted from js/src/jit-test/lib/census.js. + +this.EXPORTED_SYMBOLS = ["Census"]; + +this.Census = (function () { + const Census = {}; + + function dumpn(msg) { + dump("DBG-TEST: Census.jsm: " + msg + "\n"); + } + + // Census.walkCensus(subject, name, walker) + // + // Use |walker| to check |subject|, a census object of the sort returned by + // Debugger.Memory.prototype.takeCensus: a tree of objects with integers at the + // leaves. Use |name| as the name for |subject| in diagnostic messages. Return + // the number of leaves of |subject| we visited. + // + // A walker is an object with three methods: + // + // - enter(prop): Return the walker we should use to check the property of the + // subject census named |prop|. This is for recursing into the subobjects of + // the subject. + // + // - done(): Called after we have called 'enter' on every property of the + // subject. + // + // - check(value): Check |value|, a leaf in the subject. + // + // Walker methods are expected to simply throw if a node we visit doesn't look + // right. + Census.walkCensus = (subject, name, walker) => walk(subject, name, walker, 0); + function walk(subject, name, walker, count) { + if (typeof subject === "object") { + dumpn(name); + for (let prop in subject) { + count = walk(subject[prop], + name + "[" + uneval(prop) + "]", + walker.enter(prop), + count); + } + walker.done(); + } else { + dumpn(name + " = " + uneval(subject)); + walker.check(subject); + count++; + } + + return count; + } + + // A walker that doesn't check anything. + Census.walkAnything = { + enter: () => Census.walkAnything, + done: () => undefined, + check: () => undefined + }; + + // A walker that requires all leaves to be zeros. + Census.assertAllZeros = { + enter: () => Census.assertAllZeros, + done: () => undefined, + check: elt => { if (elt !== 0) throw new Error("Census mismatch: expected zero, found " + elt); } + }; + + function expectedObject() { + throw new Error("Census mismatch: subject has leaf where basis has nested object"); + } + + function expectedLeaf() { + throw new Error("Census mismatch: subject has nested object where basis has leaf"); + } + + // Return a function that, given a 'basis' census, returns a census walker that + // compares the subject census against the basis. The returned walker calls the + // given |compare|, |missing|, and |extra| functions as follows: + // + // - compare(subjectLeaf, basisLeaf): Check a leaf of the subject against the + // corresponding leaf of the basis. + // + // - missing(prop, value): Called when the subject is missing a property named + // |prop| which is present in the basis with value |value|. + // + // - extra(prop): Called when the subject has a property named |prop|, but the + // basis has no such property. This should return a walker that can check + // the subject's value. + function makeBasisChecker({compare, missing, extra}) { + return function makeWalker(basis) { + if (typeof basis === "object") { + var unvisited = new Set(Object.getOwnPropertyNames(basis)); + return { + enter: prop => { + unvisited.delete(prop); + if (prop in basis) { + return makeWalker(basis[prop]); + } else { + return extra(prop); + } + }, + + done: () => unvisited.forEach(prop => missing(prop, basis[prop])), + check: expectedObject + }; + } else { + return { + enter: expectedLeaf, + done: expectedLeaf, + check: elt => compare(elt, basis) + }; + } + }; + } + + function missingProp(prop) { + throw new Error("Census mismatch: subject lacks property present in basis: " + prop); + } + + function extraProp(prop) { + throw new Error("Census mismatch: subject has property not present in basis: " + prop); + } + + // Return a walker that checks that the subject census has counts all equal to + // |basis|. + Census.assertAllEqual = makeBasisChecker({ + compare: (a, b) => { if (a !== b) throw new Error("Census mismatch: expected " + a + " got " + b);}, + missing: missingProp, + extra: extraProp + }); + + function ok(val) { + if (!val) { + throw new Error("Census mismatch: expected truthy, got " + val); + } + } + + // Return a walker that checks that the subject census has at least as many + // items of each category as |basis|. + Census.assertAllNotLessThan = makeBasisChecker({ + compare: (subject, basis) => ok(subject >= basis), + missing: missingProp, + extra: () => Census.walkAnything + }); + + // Return a walker that checks that the subject census has at most as many + // items of each category as |basis|. + Census.assertAllNotMoreThan = makeBasisChecker({ + compare: (subject, basis) => ok(subject <= basis), + missing: missingProp, + extra: () => Census.walkAnything + }); + + // Return a walker that checks that the subject census has within |fudge| + // items of each category of the count in |basis|. + Census.assertAllWithin = function (fudge, basis) { + return makeBasisChecker({ + compare: (subject, basis) => ok(Math.abs(subject - basis) <= fudge), + missing: missingProp, + extra: () => Census.walkAnything + })(basis); + }; + + return Census; +}()); diff --git a/dom/heapsnapshot/tests/unit/Match.jsm b/dom/heapsnapshot/tests/unit/Match.jsm new file mode 100644 index 000000000..c29e6484e --- /dev/null +++ b/dom/heapsnapshot/tests/unit/Match.jsm @@ -0,0 +1,190 @@ +// A little pattern-matching library. +// +// Ported from js/src/tests/js1_8_5/reflect-parse/Match.js for use with devtools +// server xpcshell tests. + +this.EXPORTED_SYMBOLS = ["Match"]; + +this.Match = (function() { + + function Pattern(template) { + // act like a constructor even as a function + if (!(this instanceof Pattern)) + return new Pattern(template); + + this.template = template; + } + + Pattern.prototype = { + match: function(act) { + return match(act, this.template); + }, + + matches: function(act) { + try { + return this.match(act); + } + catch (e if e instanceof MatchError) { + return false; + } + }, + + assert: function(act, message) { + try { + return this.match(act); + } + catch (e if e instanceof MatchError) { + throw new Error((message || "failed match") + ": " + e.message); + } + }, + + toString: () => "[object Pattern]" + }; + + Pattern.ANY = new Pattern; + Pattern.ANY.template = Pattern.ANY; + + Pattern.NUMBER = new Pattern; + Pattern.NUMBER.match = function (act) { + if (typeof act !== 'number') { + throw new MatchError("Expected number, got: " + quote(act)); + } + } + + Pattern.NATURAL = new Pattern + Pattern.NATURAL.match = function (act) { + if (typeof act !== 'number' || act !== Math.floor(act) || act < 0) { + throw new MatchError("Expected natural number, got: " + quote(act)); + } + } + + var quote = uneval; + + function MatchError(msg) { + this.message = msg; + } + + MatchError.prototype = { + toString: function() { + return "match error: " + this.message; + } + }; + + function isAtom(x) { + return (typeof x === "number") || + (typeof x === "string") || + (typeof x === "boolean") || + (x === null) || + (typeof x === "object" && x instanceof RegExp); + } + + function isObject(x) { + return (x !== null) && (typeof x === "object"); + } + + function isFunction(x) { + return typeof x === "function"; + } + + function isArrayLike(x) { + return isObject(x) && ("length" in x); + } + + function matchAtom(act, exp) { + if ((typeof exp) === "number" && isNaN(exp)) { + if ((typeof act) !== "number" || !isNaN(act)) + throw new MatchError("expected NaN, got: " + quote(act)); + return true; + } + + if (exp === null) { + if (act !== null) + throw new MatchError("expected null, got: " + quote(act)); + return true; + } + + if (exp instanceof RegExp) { + if (!(act instanceof RegExp) || exp.source !== act.source) + throw new MatchError("expected " + quote(exp) + ", got: " + quote(act)); + return true; + } + + switch (typeof exp) { + case "string": + if (act !== exp) + throw new MatchError("expected " + quote(exp) + ", got " + quote(act)); + return true; + case "boolean": + case "number": + if (exp !== act) + throw new MatchError("expected " + exp + ", got " + quote(act)); + return true; + } + + throw new Error("bad pattern: " + exp.toSource()); + } + + function matchObject(act, exp) { + if (!isObject(act)) + throw new MatchError("expected object, got " + quote(act)); + + for (var key in exp) { + if (!(key in act)) + throw new MatchError("expected property " + quote(key) + " not found in " + quote(act)); + match(act[key], exp[key]); + } + + return true; + } + + function matchFunction(act, exp) { + if (!isFunction(act)) + throw new MatchError("expected function, got " + quote(act)); + + if (act !== exp) + throw new MatchError("expected function: " + exp + + "\nbut got different function: " + act); + } + + function matchArray(act, exp) { + if (!isObject(act) || !("length" in act)) + throw new MatchError("expected array-like object, got " + quote(act)); + + var length = exp.length; + if (act.length !== exp.length) + throw new MatchError("expected array-like object of length " + length + ", got " + quote(act)); + + for (var i = 0; i < length; i++) { + if (i in exp) { + if (!(i in act)) + throw new MatchError("expected array property " + i + " not found in " + quote(act)); + match(act[i], exp[i]); + } + } + + return true; + } + + function match(act, exp) { + if (exp === Pattern.ANY) + return true; + + if (exp instanceof Pattern) + return exp.match(act); + + if (isAtom(exp)) + return matchAtom(act, exp); + + if (isArrayLike(exp)) + return matchArray(act, exp); + + if (isFunction(exp)) + return matchFunction(act, exp); + + return matchObject(act, exp); + } + + return { Pattern: Pattern, + MatchError: MatchError }; + +})(); diff --git a/dom/heapsnapshot/tests/unit/dominator-tree-worker.js b/dom/heapsnapshot/tests/unit/dominator-tree-worker.js new file mode 100644 index 000000000..1f49ca841 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/dominator-tree-worker.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +console.log("Initializing worker."); + +self.onmessage = e => { + console.log("Starting test."); + try { + const path = ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true }); + const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(path); + + const dominatorTree = snapshot.computeDominatorTree(); + ok(dominatorTree); + ok(dominatorTree instanceof DominatorTree); + + let threw = false; + try { + new DominatorTree(); + } catch (e) { + threw = true; + } + ok(threw, "Constructor shouldn't be usable"); + } catch (e) { + ok(false, "Unexpected error inside worker:\n" + e.toString() + "\n" + e.stack); + } finally { + done(); + } +}; + +// Proxy assertions to the main thread. +function ok(val, msg) { + console.log("ok(" + !!val + ", \"" + msg + "\")"); + self.postMessage({ + type: "assertion", + passed: !!val, + msg, + stack: Error().stack + }); +} + +// Tell the main thread we are done with the tests. +function done() { + console.log("done()"); + self.postMessage({ + type: "done" + }); +} diff --git a/dom/heapsnapshot/tests/unit/head_heapsnapshot.js b/dom/heapsnapshot/tests/unit/head_heapsnapshot.js new file mode 100644 index 000000000..3171c8a6f --- /dev/null +++ b/dom/heapsnapshot/tests/unit/head_heapsnapshot.js @@ -0,0 +1,448 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; +var CC = Components.Constructor; + +const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const { Match } = Cu.import("resource://test/Match.jsm", {}); +const { Census } = Cu.import("resource://test/Census.jsm", {}); +const { addDebuggerToGlobal } = + Cu.import("resource://gre/modules/jsdebugger.jsm", {}); +const { Task } = require("devtools/shared/task"); + +const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const flags = require("devtools/shared/flags"); +const HeapAnalysesClient = + require("devtools/shared/heapsnapshot/HeapAnalysesClient"); +const Services = require("Services"); +const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node"); +const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils"); +const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode"); +const { deduplicatePaths } = require("devtools/shared/heapsnapshot/shortest-paths"); +const { LabelAndShallowSizeVisitor } = DominatorTreeNode; + + +// Always log packets when running tests. runxpcshelltests.py will throw +// the output away anyway, unless you give it the --verbose flag. +if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { + Services.prefs.setBoolPref("devtools.debugger.log", true); +} +flags.wantLogging = true; + +const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"] + .createInstance(Ci.nsIPrincipal); + +function dumpn(msg) { + dump("HEAPSNAPSHOT-TEST: " + msg + "\n"); +} + +function addTestingFunctionsToGlobal(global) { + global.eval( + ` + const testingFunctions = Components.utils.getJSTestingFunctions(); + for (let k in testingFunctions) { + this[k] = testingFunctions[k]; + } + ` + ); + if (!global.print) { + global.print = do_print; + } + if (!global.newGlobal) { + global.newGlobal = newGlobal; + } + if (!global.Debugger) { + addDebuggerToGlobal(global); + } +} + +addTestingFunctionsToGlobal(this); + +/** + * Create a new global, with all the JS shell testing functions. Similar to the + * newGlobal function exposed to JS shells, and useful for porting JS shell + * tests to xpcshell tests. + */ +function newGlobal() { + const global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true }); + addTestingFunctionsToGlobal(global); + return global; +} + +function assertThrowsValue(f, val, msg) { + var fullmsg; + try { + f(); + } catch (exc) { + if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val)) + return; + fullmsg = "Assertion failed: expected exception " + val + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); +} + +/** + * Returns the full path of the file with the specified name in a + * platform-independent and URL-like form. + */ +function getFilePath(aName, aAllowMissing = false, aUsePlatformPathSeparator = false) +{ + let file = do_get_file(aName, aAllowMissing); + let path = Services.io.newFileURI(file).spec; + let filePrePath = "file://"; + if ("nsILocalFileWin" in Ci && + file instanceof Ci.nsILocalFileWin) { + filePrePath += "/"; + } + + path = path.slice(filePrePath.length); + + if (aUsePlatformPathSeparator && path.match(/^\w:/)) { + path = path.replace(/\//g, "\\"); + } + + return path; +} + +function saveNewHeapSnapshot(opts = { runtime: true }) { + const filePath = ChromeUtils.saveHeapSnapshot(opts); + ok(filePath, "Should get a file path to save the core dump to."); + ok(true, "Saved a heap snapshot to " + filePath); + return filePath; +} + +function readHeapSnapshot(filePath) { + const snapshot = ChromeUtils.readHeapSnapshot(filePath); + ok(snapshot, "Should have read a heap snapshot back from " + filePath); + ok(snapshot instanceof HeapSnapshot, "snapshot should be an instance of HeapSnapshot"); + return snapshot; +} + +/** + * Save a heap snapshot to the file with the given name in the current + * directory, read it back as a HeapSnapshot instance, and then take a census of + * the heap snapshot's serialized heap graph with the provided census options. + * + * @param {Object|undefined} censusOptions + * Options that should be passed through to the takeCensus method. See + * js/src/doc/Debugger/Debugger.Memory.md for details. + * + * @param {Debugger|null} dbg + * If a Debugger object is given, only serialize the subgraph covered by + * the Debugger's debuggees. If null, serialize the whole heap graph. + * + * @param {String} fileName + * The file name to save the heap snapshot's core dump file to, within + * the current directory. + * + * @returns Census + */ +function saveHeapSnapshotAndTakeCensus(dbg = null, censusOptions = undefined) { + const snapshotOptions = dbg ? { debugger: dbg } : { runtime: true }; + const filePath = saveNewHeapSnapshot(snapshotOptions); + const snapshot = readHeapSnapshot(filePath); + + equal(typeof snapshot.takeCensus, "function", "snapshot should have a takeCensus method"); + + return snapshot.takeCensus(censusOptions); +} + +/** + * Save a heap snapshot to disk, read it back as a HeapSnapshot instance, and + * then compute its dominator tree. + * + * @param {Debugger|null} dbg + * If a Debugger object is given, only serialize the subgraph covered by + * the Debugger's debuggees. If null, serialize the whole heap graph. + * + * @returns {DominatorTree} + */ +function saveHeapSnapshotAndComputeDominatorTree(dbg = null) { + const snapshotOptions = dbg ? { debugger: dbg } : { runtime: true }; + const filePath = saveNewHeapSnapshot(snapshotOptions); + const snapshot = readHeapSnapshot(filePath); + + equal(typeof snapshot.computeDominatorTree, "function", + "snapshot should have a `computeDominatorTree` method"); + + const dominatorTree = snapshot.computeDominatorTree(); + + ok(dominatorTree, "Should be able to compute a dominator tree"); + ok(dominatorTree instanceof DominatorTree, "Should be an instance of DominatorTree"); + + return dominatorTree; +} + +function isSavedFrame(obj) { + return Object.prototype.toString.call(obj) === "[object SavedFrame]"; +} + +function savedFrameReplacer(key, val) { + if (isSavedFrame(val)) { + return `<SavedFrame '${val.toString().split(/\n/g).shift()}'>`; + } else { + return val; + } +} + +/** + * Assert that creating a CensusTreeNode from the given `report` with the + * specified `breakdown` creates the given `expected` CensusTreeNode. + * + * @param {Object} breakdown + * The census breakdown. + * + * @param {Object} report + * The census report. + * + * @param {Object} expected + * The expected CensusTreeNode result. + * + * @param {Object} options + * The options to pass through to `censusReportToCensusTreeNode`. + */ +function compareCensusViewData(breakdown, report, expected, options) { + dumpn("Generating CensusTreeNode from report:"); + dumpn("breakdown: " + JSON.stringify(breakdown, null, 4)); + dumpn("report: " + JSON.stringify(report, null, 4)); + dumpn("expected: " + JSON.stringify(expected, savedFrameReplacer, 4)); + + const actual = censusReportToCensusTreeNode(breakdown, report, options); + dumpn("actual: " + JSON.stringify(actual, savedFrameReplacer, 4)); + + assertStructurallyEquivalent(actual, expected); +} + +// Deep structural equivalence that can handle Map objects in addition to plain +// objects. +function assertStructurallyEquivalent(actual, expected, path = "root") { + if (actual === expected) { + equal(actual, expected, "actual and expected are the same"); + return; + } + + equal(typeof actual, typeof expected, `${path}: typeof should be the same`); + + if (actual && typeof actual === "object") { + const actualProtoString = Object.prototype.toString.call(actual); + const expectedProtoString = Object.prototype.toString.call(expected); + equal(actualProtoString, expectedProtoString, + `${path}: Object.prototype.toString.call() should be the same`); + + if (actualProtoString === "[object Map]") { + const expectedKeys = new Set([...expected.keys()]); + + for (let key of actual.keys()) { + ok(expectedKeys.has(key), + `${path}: every key in actual should exist in expected: ${String(key).slice(0, 10)}`); + expectedKeys.delete(key); + + assertStructurallyEquivalent(actual.get(key), expected.get(key), + path + ".get(" + String(key).slice(0, 20) + ")"); + } + + equal(expectedKeys.size, 0, + `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`); + } else if (actualProtoString === "[object Set]") { + const expectedItems = new Set([...expected]); + + for (let item of actual) { + ok(expectedItems.has(item), + `${path}: every set item in actual should exist in expected: ${item}`); + expectedItems.delete(item); + } + + equal(expectedItems.size, 0, + `${path}: every set item in expected should also exist in actual, did not see ${[...expectedItems]}`); + } else { + const expectedKeys = new Set(Object.keys(expected)); + + for (let key of Object.keys(actual)) { + ok(expectedKeys.has(key), + `${path}: every key in actual should exist in expected: ${key}`); + expectedKeys.delete(key); + + assertStructurallyEquivalent(actual[key], expected[key], path + "." + key); + } + + equal(expectedKeys.size, 0, + `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`); + } + } else { + equal(actual, expected, `${path}: primitives should be equal`); + } +} + +/** + * Assert that creating a diff of the `first` and `second` census reports + * creates the `expected` delta-report. + * + * @param {Object} breakdown + * The census breakdown. + * + * @param {Object} first + * The first census report. + * + * @param {Object} second + * The second census report. + * + * @param {Object} expected + * The expected delta-report. + */ +function assertDiff(breakdown, first, second, expected) { + dumpn("Diffing census reports:"); + dumpn("Breakdown: " + JSON.stringify(breakdown, null, 4)); + dumpn("First census report: " + JSON.stringify(first, null, 4)); + dumpn("Second census report: " + JSON.stringify(second, null, 4)); + dumpn("Expected delta-report: " + JSON.stringify(expected, null, 4)); + + const actual = CensusUtils.diff(breakdown, first, second); + dumpn("Actual delta-report: " + JSON.stringify(actual, null, 4)); + + assertStructurallyEquivalent(actual, expected); +} + +/** + * Assert that creating a label and getting a shallow size from the given node + * description with the specified breakdown is as expected. + * + * @param {Object} breakdown + * @param {Object} givenDescription + * @param {Number} expectedShallowSize + * @param {Object} expectedLabel + */ +function assertLabelAndShallowSize(breakdown, givenDescription, expectedShallowSize, expectedLabel) { + dumpn("Computing label and shallow size from node description:"); + dumpn("Breakdown: " + JSON.stringify(breakdown, null, 4)); + dumpn("Given description: " + JSON.stringify(givenDescription, null, 4)); + + const visitor = new LabelAndShallowSizeVisitor(); + CensusUtils.walk(breakdown, description, visitor); + + dumpn("Expected shallow size: " + expectedShallowSize); + dumpn("Actual shallow size: " + visitor.shallowSize()); + equal(visitor.shallowSize(), expectedShallowSize, "Shallow size should be correct"); + + dumpn("Expected label: " + JSON.stringify(expectedLabel, null, 4)); + dumpn("Actual label: " + JSON.stringify(visitor.label(), null, 4)); + assertStructurallyEquivalent(visitor.label(), expectedLabel); +} + +// Counter for mock DominatorTreeNode ids. +let TEST_NODE_ID_COUNTER = 0; + +/** + * Create a mock DominatorTreeNode for testing, with sane defaults. Override any + * property by providing it on `opts`. Optionally pass child nodes as well. + * + * @param {Object} opts + * @param {Array<DominatorTreeNode>?} children + * + * @returns {DominatorTreeNode} + */ +function makeTestDominatorTreeNode(opts, children) { + const nodeId = TEST_NODE_ID_COUNTER++; + + const node = Object.assign({ + nodeId, + label: undefined, + shallowSize: 1, + retainedSize: (children || []).reduce((size, c) => size + c.retainedSize, 1), + parentId: undefined, + children, + moreChildrenAvailable: true, + }, opts); + + if (children && children.length) { + children.map(c => c.parentId = node.nodeId); + } + + return node; +} + +/** + * Insert `newChildren` into the given dominator `tree` as specified by the + * `path` from the root to the node the `newChildren` should be inserted + * beneath. Assert that the resulting tree matches `expected`. + */ +function assertDominatorTreeNodeInsertion(tree, path, newChildren, moreChildrenAvailable, expected) { + dumpn("Inserting new children into a dominator tree:"); + dumpn("Dominator tree: " + JSON.stringify(tree, null, 2)); + dumpn("Path: " + JSON.stringify(path, null, 2)); + dumpn("New children: " + JSON.stringify(newChildren, null, 2)); + dumpn("Expected resulting tree: " + JSON.stringify(expected, null, 2)); + + const actual = DominatorTreeNode.insert(tree, path, newChildren, moreChildrenAvailable); + dumpn("Actual resulting tree: " + JSON.stringify(actual, null, 2)); + + assertStructurallyEquivalent(actual, expected); +} + +function assertDeduplicatedPaths({ target, paths, expectedNodes, expectedEdges }) { + dumpn("Deduplicating paths:"); + dumpn("target = " + target); + dumpn("paths = " + JSON.stringify(paths, null, 2)); + dumpn("expectedNodes = " + expectedNodes); + dumpn("expectedEdges = " + JSON.stringify(expectedEdges, null, 2)); + + const { nodes, edges } = deduplicatePaths(target, paths); + + dumpn("Actual nodes = " + nodes); + dumpn("Actual edges = " + JSON.stringify(edges, null, 2)); + + equal(nodes.length, expectedNodes.length, + "actual number of nodes is equal to the expected number of nodes"); + + equal(edges.length, expectedEdges.length, + "actual number of edges is equal to the expected number of edges"); + + const expectedNodeSet = new Set(expectedNodes); + const nodeSet = new Set(nodes); + ok(nodeSet.size === nodes.length, + "each returned node should be unique"); + + for (let node of nodes) { + ok(expectedNodeSet.has(node), `the ${node} node was expected`); + } + + for (let expectedEdge of expectedEdges) { + let count = 0; + for (let edge of edges) { + if (edge.from === expectedEdge.from && + edge.to === expectedEdge.to && + edge.name === expectedEdge.name) { + count++; + } + } + equal(count, 1, + "should have exactly one matching edge for the expected edge = " + JSON.stringify(edge)); + } +} + +function assertCountToBucketBreakdown(breakdown, expected) { + dumpn("count => bucket breakdown"); + dumpn("Initial breakdown = ", JSON.stringify(breakdown, null, 2)); + dumpn("Expected results = ", JSON.stringify(expected, null, 2)); + + const actual = CensusUtils.countToBucketBreakdown(breakdown); + dumpn("Actual results = ", JSON.stringify(actual, null, 2)); + + assertStructurallyEquivalent(actual, expected); +} + +/** + * Create a mock path entry for the given predecessor and edge. + */ +function pathEntry(predecessor, edge) { + return { predecessor, edge }; +} diff --git a/dom/heapsnapshot/tests/unit/heap-snapshot-worker.js b/dom/heapsnapshot/tests/unit/heap-snapshot-worker.js new file mode 100644 index 000000000..10ee70cec --- /dev/null +++ b/dom/heapsnapshot/tests/unit/heap-snapshot-worker.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +console.log("Initializing worker."); + +self.onmessage = e => { + console.log("Starting test."); + try { + ok(typeof ChromeUtils === "undefined", + "Should not have access to ChromeUtils in a worker."); + ok(ThreadSafeChromeUtils, + "Should have access to ThreadSafeChromeUtils in a worker."); + ok(HeapSnapshot, + "Should have access to HeapSnapshot in a worker."); + + const filePath = ThreadSafeChromeUtils.saveHeapSnapshot({ globals: [this] }); + ok(true, "Should be able to save a snapshot."); + + const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(filePath); + ok(snapshot, "Should be able to read a heap snapshot"); + ok(snapshot instanceof HeapSnapshot, "Should be an instanceof HeapSnapshot"); + } catch (e) { + ok(false, "Unexpected error inside worker:\n" + e.toString() + "\n" + e.stack); + } finally { + done(); + } +}; + +// Proxy assertions to the main thread. +function ok(val, msg) { + console.log("ok(" + !!val + ", \"" + msg + "\")"); + self.postMessage({ + type: "assertion", + passed: !!val, + msg, + stack: Error().stack + }); +} + +// Tell the main thread we are done with the tests. +function done() { + console.log("done()"); + self.postMessage({ + type: "done" + }); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js new file mode 100644 index 000000000..845a0d263 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can generate label structures from node description reports. + +const breakdown = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +const description = { + objects: { + Function: { count: 1, bytes: 32 }, + other: { count: 0, bytes: 0 } + }, + strings: {}, + scripts: {}, + other: {} +}; + +const expected = [ + "objects", + "Function" +]; + +const shallowSize = 32; + +function run_test() { + assertLabelAndShallowSize(breakdown, description, shallowSize, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js new file mode 100644 index 000000000..e1f32de58 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can generate label structures from node description reports. + +const breakdown = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +const description = { + objects: { + other: { count: 1, bytes: 10 } + }, + strings: {}, + scripts: {}, + other: {} +}; + +const expected = [ + "objects", + "other" +]; + +const shallowSize = 10; + +function run_test() { + assertLabelAndShallowSize(breakdown, description, shallowSize, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js new file mode 100644 index 000000000..ad35dcec1 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can generate label structures from node description reports. + +const breakdown = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +const description = { + objects: { + other: { count: 0, bytes: 0 } + }, + strings: { + "JSString": { count: 1, bytes: 42 }, + }, + scripts: {}, + other: {} +}; + +const expected = [ + "strings", + "JSString" +]; + +const shallowSize = 42; + +function run_test() { + assertLabelAndShallowSize(breakdown, description, shallowSize, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js new file mode 100644 index 000000000..566ad0dab --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can generate label structures from node description reports. + +const breakdown = { + by: "coarseType", + objects: { + by: "objectClass", + then: { + by: "allocationStack", + then: { by: "count", count: true, bytes: true }, + noStack: { by: "count", count: true, bytes: true }, + }, + other: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +const stack = saveStack(); + +const description = { + objects: { + Array: new Map([[stack, { count: 1, bytes: 512 }]]), + other: { count: 0, bytes: 0 } + }, + strings: {}, + scripts: {}, + other: {} +}; + +const expected = [ + "objects", + "Array", + stack +]; + +const shallowSize = 512; + +function run_test() { + assertLabelAndShallowSize(breakdown, description, shallowSize, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_attachShortestPaths_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_attachShortestPaths_01.js new file mode 100644 index 000000000..24e8e2eb5 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_attachShortestPaths_01.js @@ -0,0 +1,132 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the DominatorTreeNode.attachShortestPaths function can correctly +// attach the deduplicated shortest retaining paths for each node it is given. + +const startNodeId = 9999; +const maxNumPaths = 2; + +// Mock data mapping node id to shortest paths to that node id. +const shortestPaths = new Map([ + [1000, [ + [pathEntry(1100, "a"), pathEntry(1200, "b")], + [pathEntry(1100, "c"), pathEntry(1300, "d")], + ]], + [2000, [ + [pathEntry(2100, "e"), pathEntry(2200, "f"), pathEntry(2300, "g")] + ]], + [3000, [ + [pathEntry(3100, "h")], + [pathEntry(3100, "i")], + [pathEntry(3100, "j")], + [pathEntry(3200, "k")], + [pathEntry(3300, "l")], + [pathEntry(3400, "m")], + ]], +]); + +const actual = [ + makeTestDominatorTreeNode({ nodeId: 1000 }), + makeTestDominatorTreeNode({ nodeId: 2000 }), + makeTestDominatorTreeNode({ nodeId: 3000 }), +]; + +const expected = [ + makeTestDominatorTreeNode({ + nodeId: 1000, + shortestPaths: { + nodes: [ + { id: 1000, label: ["SomeType-1000"] }, + { id: 1100, label: ["SomeType-1100"] }, + { id: 1200, label: ["SomeType-1200"] }, + { id: 1300, label: ["SomeType-1300"] }, + ], + edges: [ + { from: 1100, to: 1200, name: "a" }, + { from: 1100, to: 1300, name: "c" }, + { from: 1200, to: 1000, name: "b" }, + { from: 1300, to: 1000, name: "d" }, + ] + } + }), + + makeTestDominatorTreeNode({ + nodeId: 2000, + shortestPaths: { + nodes: [ + { id: 2000, label: ["SomeType-2000"] }, + { id: 2100, label: ["SomeType-2100"] }, + { id: 2200, label: ["SomeType-2200"] }, + { id: 2300, label: ["SomeType-2300"] }, + ], + edges: [ + { from: 2100, to: 2200, name: "e" }, + { from: 2200, to: 2300, name: "f" }, + { from: 2300, to: 2000, name: "g" }, + ] + } + }), + + makeTestDominatorTreeNode({ nodeId: 3000, + shortestPaths: { + nodes: [ + { id: 3000, label: ["SomeType-3000"] }, + { id: 3100, label: ["SomeType-3100"] }, + { id: 3200, label: ["SomeType-3200"] }, + { id: 3300, label: ["SomeType-3300"] }, + { id: 3400, label: ["SomeType-3400"] }, + ], + edges: [ + { from: 3100, to: 3000, name: "h" }, + { from: 3100, to: 3000, name: "i" }, + { from: 3100, to: 3000, name: "j" }, + { from: 3200, to: 3000, name: "k" }, + { from: 3300, to: 3000, name: "l" }, + { from: 3400, to: 3000, name: "m" }, + ] + } + }), +]; + +const breakdown = { + by: "internalType", + then: { by: "count", count: true, bytes: true } +}; + +const mockSnapshot = { + computeShortestPaths: (start, nodeIds, max) => { + equal(start, startNodeId); + equal(max, maxNumPaths); + + return new Map(nodeIds.map(nodeId => { + const paths = shortestPaths.get(nodeId); + ok(paths, "Expected computeShortestPaths call for node id = " + nodeId); + return [nodeId, paths]; + })); + }, + + describeNode: (bd, nodeId) => { + equal(bd, breakdown); + return { + ["SomeType-" + nodeId]: { + count: 1, + bytes: 10, + } + }; + }, +}; + +function run_test() { + DominatorTreeNode.attachShortestPaths(mockSnapshot, + breakdown, + startNodeId, + actual, + maxNumPaths); + + dumpn("Expected = " + JSON.stringify(expected, null, 2)); + dumpn("Actual = " + JSON.stringify(actual, null, 2)); + + assertStructurallyEquivalent(expected, actual); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_getNodeByIdAlongPath_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_getNodeByIdAlongPath_01.js new file mode 100644 index 000000000..de2907809 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_getNodeByIdAlongPath_01.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can find the node with the given id along the specified path. + +const node3000 = makeTestDominatorTreeNode({ nodeId: 3000 }); + +const node2000 = makeTestDominatorTreeNode({ nodeId: 2000 }, [ + makeTestDominatorTreeNode({}), + node3000, + makeTestDominatorTreeNode({}), +]); + +const node1000 = makeTestDominatorTreeNode({ nodeId: 1000 }, [ + makeTestDominatorTreeNode({}), + node2000, + makeTestDominatorTreeNode({}), +]); + +const tree = node1000; + +const path = [1000, 2000, 3000]; + +const tests = [ + { id: 1000, expected: node1000 }, + { id: 2000, expected: node2000 }, + { id: 3000, expected: node3000 }, +]; + +function run_test() { + for (let { id, expected } of tests) { + const actual = DominatorTreeNode.getNodeByIdAlongPath(id, tree, path); + equal(actual, expected, `We should have got the node with id = ${id}`); + } + + equal(null, + DominatorTreeNode.getNodeByIdAlongPath(999999999999, tree, path), + "null is returned for nodes that are not even in the tree"); + + const lastNodeId = tree.children[tree.children.length - 1].nodeId; + equal(null, + DominatorTreeNode.getNodeByIdAlongPath(lastNodeId, tree, path), + "null is returned for nodes that are not along the path"); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_01.js new file mode 100644 index 000000000..979232ff4 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_01.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can insert new children into an existing DominatorTreeNode tree. + +const tree = makeTestDominatorTreeNode({ nodeId: 1000 }, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({ nodeId: 2000 }, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({ nodeId: 3000 }), + makeTestDominatorTreeNode({}), + ]), + makeTestDominatorTreeNode({}), +]); + +const path = [1000, 2000, 3000]; + +const newChildren = [ + makeTestDominatorTreeNode({ parentId: 3000 }), + makeTestDominatorTreeNode({ parentId: 3000 }), +]; + +const moreChildrenAvailable = false; + +const expected = { + nodeId: 1000, + parentId: undefined, + label: undefined, + shallowSize: 1, + retainedSize: 7, + children: [ + { + nodeId: 0, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 1000, + moreChildrenAvailable: true, + children: undefined, + }, + { + nodeId: 2000, + label: undefined, + shallowSize: 1, + retainedSize: 4, + parentId: 1000, + children: [ + { + nodeId: 1, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 2000, + moreChildrenAvailable: true, + children: undefined, + }, + { + nodeId: 3000, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 2000, + children: [ + { + nodeId: 7, + parentId: 3000, + label: undefined, + shallowSize: 1, + retainedSize: 1, + moreChildrenAvailable: true, + children: undefined, + }, + { + nodeId: 8, + parentId: 3000, + label: undefined, + shallowSize: 1, + retainedSize: 1, + moreChildrenAvailable: true, + children: undefined, + }, + ], + moreChildrenAvailable: false + }, + { + nodeId: 3, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 2000, + moreChildrenAvailable: true, + children: undefined, + }, + ], + moreChildrenAvailable: true + }, + { + nodeId: 5, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 1000, + moreChildrenAvailable: true, + children: undefined, + } + ], + moreChildrenAvailable: true +}; + +function run_test() { + assertDominatorTreeNodeInsertion(tree, path, newChildren, moreChildrenAvailable, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_02.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_02.js new file mode 100644 index 000000000..9a8d11d0b --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_02.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test attempting to insert new children into an existing DominatorTreeNode +// tree with a bad path. + +const tree = makeTestDominatorTreeNode({}, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}), + ]), + makeTestDominatorTreeNode({}), +]); + +const path = [111111, 222222, 333333]; + +const newChildren = [ + makeTestDominatorTreeNode({ parentId: 333333 }), + makeTestDominatorTreeNode({ parentId: 333333 }), +]; + +const moreChildrenAvailable = false; + +const expected = tree; + +function run_test() { + assertDominatorTreeNodeInsertion(tree, path, newChildren, moreChildrenAvailable, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_03.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_03.js new file mode 100644 index 000000000..f8cb5eec3 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_insert_03.js @@ -0,0 +1,117 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test inserting new children into an existing DominatorTreeNode at the root. + +const tree = makeTestDominatorTreeNode({ nodeId: 666 }, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}, [ + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}), + makeTestDominatorTreeNode({}), + ]), + makeTestDominatorTreeNode({}), +]); + +const path = [666]; + +const newChildren = [ + makeTestDominatorTreeNode({ + nodeId: 777, + parentId: 666 + }), + makeTestDominatorTreeNode({ + nodeId: 888, + parentId: 666 + }), +]; + +const moreChildrenAvailable = false; + +const expected = { + nodeId: 666, + label: undefined, + parentId: undefined, + shallowSize: 1, + retainedSize: 7, + children: [ + { + nodeId: 0, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 666, + moreChildrenAvailable: true, + children: undefined + }, + { + nodeId: 4, + label: undefined, + shallowSize: 1, + retainedSize: 4, + parentId: 666, + children: [ + { + nodeId: 1, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 4, + moreChildrenAvailable: true, + children: undefined + }, + { + nodeId: 2, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 4, + moreChildrenAvailable: true, + children: undefined + }, + { + nodeId: 3, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 4, + moreChildrenAvailable: true, + children: undefined + } + ], + moreChildrenAvailable: true + }, + { + nodeId: 5, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 666, + moreChildrenAvailable: true, + children: undefined + }, + { + nodeId: 777, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 666, + moreChildrenAvailable: true, + children: undefined + }, + { + nodeId: 888, + label: undefined, + shallowSize: 1, + retainedSize: 1, + parentId: 666, + moreChildrenAvailable: true, + children: undefined + } + ], + moreChildrenAvailable: false +}; + +function run_test() { + assertDominatorTreeNodeInsertion(tree, path, newChildren, moreChildrenAvailable, expected); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js new file mode 100644 index 000000000..78ec47b64 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js @@ -0,0 +1,164 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we correctly set `moreChildrenAvailable` when doing a partial +// traversal of a dominator tree to create the initial incrementally loaded +// `DominatorTreeNode` tree. + +// `tree` maps parent to children: +// +// 100 +// |- 200 +// | |- 500 +// | |- 600 +// | `- 700 +// |- 300 +// | |- 800 +// | |- 900 +// `- 400 +// |- 1000 +// |- 1100 +// `- 1200 +const tree = new Map([ + [100, [200, 300, 400]], + [200, [500, 600, 700]], + [300, [800, 900]], + [400, [1000, 1100, 1200]] +]); + +const mockDominatorTree = { + root: 100, + getRetainedSize: _ => 10, + getImmediatelyDominated: id => (tree.get(id) || []).slice() +}; + +const mockSnapshot = { + describeNode: _ => ({ + objects: { count: 0, bytes: 0 }, + strings: { count: 0, bytes: 0 }, + scripts: { count: 0, bytes: 0 }, + other: { SomeType: { count: 1, bytes: 10 } } + }) +}; + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true } + }, +}; + +const expected = { + nodeId: 100, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + shortestPaths: undefined, + children: [ + { + nodeId: 200, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 100, + shortestPaths: undefined, + children: [ + { + nodeId: 500, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 200, + moreChildrenAvailable: false, + shortestPaths: undefined, + children: undefined + }, + { + nodeId: 600, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 200, + moreChildrenAvailable: false, + shortestPaths: undefined, + children: undefined + } + ], + moreChildrenAvailable: true + }, + { + nodeId: 300, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 100, + shortestPaths: undefined, + children: [ + { + nodeId: 800, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 300, + moreChildrenAvailable: false, + shortestPaths: undefined, + children: undefined + }, + { + nodeId: 900, + label: [ + "other", + "SomeType" + ], + shallowSize: 10, + retainedSize: 10, + parentId: 300, + moreChildrenAvailable: false, + shortestPaths: undefined, + children: undefined + } + ], + moreChildrenAvailable: false + } + ], + moreChildrenAvailable: true, + parentId: undefined, +}; + +function run_test() { + // Traverse the whole depth of the test tree, but one short of the number of + // siblings. This will exercise the moreChildrenAvailable handling for + // siblings. + const actual = DominatorTreeNode.partialTraversal(mockDominatorTree, + mockSnapshot, + breakdown, + /* maxDepth = */ 4, + /* siblings = */ 2); + + dumpn("Expected = " + JSON.stringify(expected, null, 2)); + dumpn("Actual = " + JSON.stringify(actual, null, 2)); + + assertStructurallyEquivalent(expected, actual); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_01.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_01.js new file mode 100644 index 000000000..e8145f658 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_01.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Sanity test that we can compute dominator trees. + +function run_test() { + const path = ChromeUtils.saveHeapSnapshot({ runtime: true }); + const snapshot = ChromeUtils.readHeapSnapshot(path); + + const dominatorTree = snapshot.computeDominatorTree(); + ok(dominatorTree); + ok(dominatorTree instanceof DominatorTree); + + let threw = false; + try { + new DominatorTree(); + } catch (e) { + threw = true; + } + ok(threw, "Constructor shouldn't be usable"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_02.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_02.js new file mode 100644 index 000000000..a518f8a27 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_02.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can compute dominator trees from a snapshot in a worker. + +add_task(function* () { + const worker = new ChromeWorker("resource://test/dominator-tree-worker.js"); + worker.postMessage({}); + + let assertionCount = 0; + worker.onmessage = e => { + if (e.data.type !== "assertion") { + return; + } + + ok(e.data.passed, e.data.msg + "\n" + e.data.stack); + assertionCount++; + }; + + yield waitForDone(worker); + + ok(assertionCount > 0); + worker.terminate(); +}); + +function waitForDone(w) { + return new Promise((resolve, reject) => { + w.onerror = e => { + reject(); + ok(false, "Error in worker: " + e); + }; + + w.addEventListener("message", function listener(e) { + if (e.data.type === "done") { + w.removeEventListener("message", listener, false); + resolve(); + } + }, false); + }); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_03.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_03.js new file mode 100644 index 000000000..0a14ce53d --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_03.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can get the root of dominator trees. + +function run_test() { + const dominatorTree = saveHeapSnapshotAndComputeDominatorTree(); + equal(typeof dominatorTree.root, "number", "root should be a number"); + equal(Math.floor(dominatorTree.root), dominatorTree.root, "root should be an integer"); + ok(dominatorTree.root >= 0, "root should be positive"); + ok(dominatorTree.root <= Math.pow(2, 48), "root should be less than or equal to 2^48"); + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_04.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_04.js new file mode 100644 index 000000000..e5aef3fec --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_04.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can get the retained sizes of dominator trees. + +function run_test() { + const dominatorTree = saveHeapSnapshotAndComputeDominatorTree(); + equal(typeof dominatorTree.getRetainedSize, "function", + "getRetainedSize should be a function"); + + const size = dominatorTree.getRetainedSize(dominatorTree.root); + ok(size, "should get a size for the root"); + equal(typeof size, "number", "retained sizes should be a number"); + equal(Math.floor(size), size, "size should be an integer"); + ok(size > 0, "size should be positive"); + ok(size <= Math.pow(2, 64), "size should be less than or equal to 2^64"); + + const bad = dominatorTree.getRetainedSize(1); + equal(bad, null, "null is returned for unknown node ids"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_05.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_05.js new file mode 100644 index 000000000..c07cee994 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_05.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can get the set of immediately dominated nodes for any given +// node and that this forms a tree. + +function run_test() { + var dominatorTree = saveHeapSnapshotAndComputeDominatorTree(); + equal(typeof dominatorTree.getImmediatelyDominated, "function", + "getImmediatelyDominated should be a function"); + + // Do a traversal of the dominator tree. + // + // Note that we don't assert directly, only if we get an unexpected + // value. There are just way too many nodes in the heap graph to assert for + // every one. This test would constantly time out and assertion messages would + // overflow the log size. + + var root = dominatorTree.root; + equal(dominatorTree.getImmediateDominator(root), null, + "The root should not have a parent"); + + var seen = new Set(); + var stack = [root]; + while (stack.length > 0) { + var top = stack.pop(); + + if (seen.has(top)) { + ok(false, + "This is a tree, not a graph: we shouldn't have multiple edges to the same node"); + } + seen.add(top); + if (seen.size % 1000 === 0) { + dumpn("Progress update: seen size = " + seen.size); + } + + var newNodes = dominatorTree.getImmediatelyDominated(top); + if (Object.prototype.toString.call(newNodes) !== "[object Array]") { + ok(false, "getImmediatelyDominated should return an array for known node ids"); + } + + var topSize = dominatorTree.getRetainedSize(top); + + var lastSize = Infinity; + for (var i = 0; i < newNodes.length; i++) { + if (typeof newNodes[i] !== "number") { + ok(false, "Every dominated id should be a number"); + } + + if (dominatorTree.getImmediateDominator(newNodes[i]) !== top) { + ok(false, "child's parent should be the expected parent"); + } + + var thisSize = dominatorTree.getRetainedSize(newNodes[i]); + + if (thisSize >= topSize) { + ok(false, "the size of children in the dominator tree should always be less than that of their parent"); + } + + if (thisSize > lastSize) { + ok(false, + "children should be sorted by greatest to least retained size, " + + "lastSize = " + lastSize + ", thisSize = " + thisSize); + } + + lastSize = thisSize; + stack.push(newNodes[i]); + } + } + + ok(true, "Successfully walked the tree"); + dumpn("Walked " + seen.size + " nodes"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_DominatorTree_06.js b/dom/heapsnapshot/tests/unit/test_DominatorTree_06.js new file mode 100644 index 000000000..680478623 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_DominatorTree_06.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the retained size of a node is the sum of its children retained +// sizes plus its shallow size. + +// Note that we don't assert directly, only if we get an unexpected +// value. There are just way too many nodes in the heap graph to assert for +// every one. This test would constantly time out and assertion messages would +// overflow the log size. +function fastAssert(cond, msg) { + if (!cond) { + ok(false, msg); + } +} + +var COUNT = { by: "count", count: false, bytes: true }; + +function run_test() { + var path = saveNewHeapSnapshot(); + var snapshot = ChromeUtils.readHeapSnapshot(path); + var dominatorTree = snapshot.computeDominatorTree(); + + // Do a traversal of the dominator tree and assert the relationship between + // retained size, shallow size, and children's retained sizes. + + var root = dominatorTree.root; + var stack = [root]; + while (stack.length > 0) { + var top = stack.pop(); + + var children = dominatorTree.getImmediatelyDominated(top); + + var topRetainedSize = dominatorTree.getRetainedSize(top); + var topShallowSize = snapshot.describeNode(COUNT, top).bytes; + fastAssert(topShallowSize <= topRetainedSize, + "The shallow size should be less than or equal to the " + + "retained size"); + + var sumOfChildrensRetainedSizes = 0; + for (var i = 0; i < children.length; i++) { + sumOfChildrensRetainedSizes += dominatorTree.getRetainedSize(children[i]); + stack.push(children[i]); + } + + fastAssert(sumOfChildrensRetainedSizes <= topRetainedSize, + "The sum of the children's retained sizes should be less than " + + "or equal to the retained size"); + fastAssert(sumOfChildrensRetainedSizes + topShallowSize === topRetainedSize, + "The sum of the children's retained sizes plus the shallow " + + "size should be equal to the retained size"); + } + + ok(true, "Successfully walked the tree"); + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_01.js new file mode 100644 index 000000000..0114e0b69 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_01.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the HeapAnalyses{Client,Worker} "computeDominatorTree" request. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + equal(typeof dominatorTreeId, "number", + "should get a dominator tree id, and it should be a number"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_02.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_02.js new file mode 100644 index 000000000..6e3f5b257 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_02.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the HeapAnalyses{Client,Worker} "computeDominatorTree" request with bad +// file paths. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + let threw = false; + try { + yield client.computeDominatorTree("/etc/passwd"); + } catch (_) { + threw = true; + } + ok(threw, "should throw when given a bad path"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_01.js new file mode 100644 index 000000000..7708de93c --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_01.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can delete heap snapshots. + +function run_test() { + run_next_test(); +} + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + let dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + ok(true, "Should have computed the dominator tree"); + + yield client.deleteHeapSnapshot(snapshotFilePath); + ok(true, "Should have deleted the snapshot"); + + let threw = false; + try { + yield client.getDominatorTree({ + dominatorTreeId: dominatorTreeId, + breakdown + }); + } catch (_) { + threw = true; + } + ok(threw, "getDominatorTree on deleted tree should throw an error"); + + threw = false; + try { + yield client.computeDominatorTree(snapshotFilePath); + } catch (_) { + threw = true; + } + ok(threw, "computeDominatorTree on deleted snapshot should throw an error"); + + threw = false; + try { + yield client.takeCensus(snapshotFilePath); + } catch (_) { + threw = true; + } + ok(threw, "takeCensus on deleted tree should throw an error"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_02.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_02.js new file mode 100644 index 000000000..3e25ddac4 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_02.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test deleteHeapSnapshot is a noop if the provided path matches no snapshot + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + let threw = false; + try { + yield client.deleteHeapSnapshot("path-does-not-exist"); + } catch (_) { + threw = true; + } + ok(threw, "deleteHeapSnapshot on non-existant path should throw an error"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js new file mode 100644 index 000000000..e648c9407 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test other dominatorTrees can still be retrieved after deleting a snapshot + +function run_test() { + run_next_test(); +} + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +function* createSnapshotAndDominatorTree(client) { + let snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + let dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + return { dominatorTreeId, snapshotFilePath }; +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + let savedSnapshots = [ + yield createSnapshotAndDominatorTree(client), + yield createSnapshotAndDominatorTree(client), + yield createSnapshotAndDominatorTree(client) + ]; + ok(true, "Create 3 snapshots and dominator trees"); + + yield client.deleteHeapSnapshot(savedSnapshots[1].snapshotFilePath); + ok(true, "Snapshot deleted"); + + let tree = yield client.getDominatorTree({ + dominatorTreeId: savedSnapshots[0].dominatorTreeId, + breakdown + }); + ok(tree, "Should get a valid tree for first snapshot"); + + let threw = false; + try { + yield client.getDominatorTree({ + dominatorTreeId: savedSnapshots[1].dominatorTreeId, + breakdown + }); + } catch (_) { + threw = true; + } + ok(threw, "getDominatorTree on a deleted snapshot should throw an error"); + + tree = yield client.getDominatorTree({ + dominatorTreeId: savedSnapshots[2].dominatorTreeId, + breakdown + }); + ok(tree, "Should get a valid tree for third snapshot"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js new file mode 100644 index 000000000..b63ad4230 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can get census individuals. + +function run_test() { + run_next_test(); +} + +const COUNT = { by: "count", count: true, bytes: true }; + +const CENSUS_BREAKDOWN = { + by: "coarseType", + objects: COUNT, + strings: COUNT, + scripts: COUNT, + other: COUNT, +}; + +const LABEL_BREAKDOWN = { + by: "internalType", + then: COUNT, +}; + +const MAX_INDIVIDUALS = 10; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + ok(true, "Should have computed dominator tree"); + + const { report } = yield client.takeCensus(snapshotFilePath, + { breakdown: CENSUS_BREAKDOWN }, + { asTreeNode: true }); + ok(report, "Should get a report"); + + let nodesWithLeafIndicesFound = 0; + + yield* (function* assertCanGetIndividuals(censusNode) { + if (censusNode.reportLeafIndex !== undefined) { + nodesWithLeafIndicesFound++; + + const response = yield client.getCensusIndividuals({ + dominatorTreeId, + indices: DevToolsUtils.isSet(censusNode.reportLeafIndex) + ? censusNode.reportLeafIndex + : new Set([censusNode.reportLeafIndex]), + censusBreakdown: CENSUS_BREAKDOWN, + labelBreakdown: LABEL_BREAKDOWN, + maxRetainingPaths: 1, + maxIndividuals: MAX_INDIVIDUALS, + }); + + dumpn(`response = ${JSON.stringify(response, null, 4)}`); + + equal(response.nodes.length, Math.min(MAX_INDIVIDUALS, censusNode.count), + "response.nodes.length === Math.min(MAX_INDIVIDUALS, censusNode.count)"); + + let lastRetainedSize = Infinity; + for (let individual of response.nodes) { + equal(typeof individual.nodeId, "number", + "individual.nodeId should be a number"); + ok(individual.retainedSize <= lastRetainedSize, + "individual.retainedSize <= lastRetainedSize"); + lastRetainedSize = individual.retainedSize; + ok(individual.shallowSize, "individual.shallowSize should exist and be non-zero"); + ok(individual.shortestPaths, "individual.shortestPaths should exist"); + ok(individual.shortestPaths.nodes, "individual.shortestPaths.nodes should exist"); + ok(individual.shortestPaths.edges, "individual.shortestPaths.edges should exist"); + ok(individual.label, "individual.label should exist"); + } + } + + if (censusNode.children) { + for (let child of censusNode.children) { + yield* assertCanGetIndividuals(child); + } + } + }(report)); + + equal(nodesWithLeafIndicesFound, 4, "Should have found a leaf for each coarse type"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCreationTime_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCreationTime_01.js new file mode 100644 index 000000000..5df79de7a --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getCreationTime_01.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can get a HeapSnapshot's +// creation time. + +function waitForThirtyMilliseconds() { + const start = Date.now(); + while (Date.now() - start < 30) ; +} + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "internalType", + then: { by: "count", count: true, bytes: true } +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + const start = Date.now() * 1000; + + // Because Date.now() is less precise than the snapshot's time stamp, give it + // a little bit of head room. Additionally, WinXP's timers have a granularity + // of only +/-15 ms. + waitForThirtyMilliseconds(); + const snapshotFilePath = saveNewHeapSnapshot(); + waitForThirtyMilliseconds(); + const end = Date.now() * 1000; + + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + let threw = false; + try { + yield client.getCreationTime("/not/a/real/path", { + breakdown: BREAKDOWN + }); + } catch (_) { + threw = true; + } + ok(threw, "getCreationTime should throw when snapshot does not exist"); + + let time = yield client.getCreationTime(snapshotFilePath, { + breakdown: BREAKDOWN + }); + + dumpn("Start = " + start); + dumpn("End = " + end); + dumpn("Time = " + time); + + ok(time >= start, "creation time occurred after start"); + ok(time <= end, "creation time occurred before end"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js new file mode 100644 index 000000000..cedea5375 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the HeapAnalyses{Client,Worker} "getDominatorTree" request. + +function run_test() { + run_next_test(); +} + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + equal(typeof dominatorTreeId, "number", + "should get a dominator tree id, and it should be a number"); + + const partialTree = yield client.getDominatorTree({ + dominatorTreeId, + breakdown + }); + ok(partialTree, "Should get a partial tree"); + equal(typeof partialTree, "object", + "partialTree should be an object"); + + function checkTree(node) { + equal(typeof node.nodeId, "number", "each node should have an id"); + + if (node === partialTree) { + equal(node.parentId, undefined, "the root has no parent"); + } else { + equal(typeof node.parentId, "number", "each node should have a parent id"); + } + + equal(typeof node.retainedSize, "number", + "each node should have a retained size"); + + ok(node.children === undefined || Array.isArray(node.children), + "each node either has a list of children, or undefined meaning no children loaded"); + equal(typeof node.moreChildrenAvailable, "boolean", + "each node should indicate if there are more children available or not"); + + equal(typeof node.shortestPaths, "object", + "Should have shortest paths"); + equal(typeof node.shortestPaths.nodes, "object", + "Should have shortest paths' nodes"); + equal(typeof node.shortestPaths.edges, "object", + "Should have shortest paths' edges"); + + if (node.children) { + node.children.forEach(checkTree); + } + } + + checkTree(partialTree); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_02.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_02.js new file mode 100644 index 000000000..fd29beece --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_02.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the HeapAnalyses{Client,Worker} "getDominatorTree" request with bad +// dominator tree ids. + +function run_test() { + run_next_test(); +} + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + let threw = false; + try { + yield client.getDominatorTree({ dominatorTreeId: 42, breakdown }); + } catch (_) { + threw = true; + } + ok(threw, "should throw when given a bad id"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js new file mode 100644 index 000000000..caf1c2056 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js @@ -0,0 +1,81 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the HeapAnalyses{Client,Worker} "getImmediatelyDominated" request. + +function run_test() { + run_next_test(); +} + +const breakdown = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath); + + const partialTree = yield client.getDominatorTree({ + dominatorTreeId, + breakdown + }); + ok(partialTree.children.length > 0, + "root should immediately dominate some nodes"); + + // First, test getting a subset of children available. + const response = yield client.getImmediatelyDominated({ + dominatorTreeId, + breakdown, + nodeId: partialTree.nodeId, + startIndex: 0, + maxCount: partialTree.children.length - 1 + }); + + ok(Array.isArray(response.nodes)); + ok(response.nodes.every(node => node.parentId === partialTree.nodeId)); + ok(response.moreChildrenAvailable); + equal(response.path.length, 1); + equal(response.path[0], partialTree.nodeId); + + for (let node of response.nodes) { + equal(typeof node.shortestPaths, "object", + "Should have shortest paths"); + equal(typeof node.shortestPaths.nodes, "object", + "Should have shortest paths' nodes"); + equal(typeof node.shortestPaths.edges, "object", + "Should have shortest paths' edges"); + } + + // Next, test getting a subset of children available. + const secondResponse = yield client.getImmediatelyDominated({ + dominatorTreeId, + breakdown, + nodeId: partialTree.nodeId, + startIndex: 0, + maxCount: Infinity + }); + + ok(Array.isArray(secondResponse.nodes)); + ok(secondResponse.nodes.every(node => node.parentId === partialTree.nodeId)); + ok(!secondResponse.moreChildrenAvailable); + equal(secondResponse.path.length, 1); + equal(secondResponse.path[0], partialTree.nodeId); + + for (let node of secondResponse.nodes) { + equal(typeof node.shortestPaths, "object", + "Should have shortest paths"); + equal(typeof node.shortestPaths.nodes, "object", + "Should have shortest paths' nodes"); + equal(typeof node.shortestPaths.edges, "object", + "Should have shortest paths' edges"); + } + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_readHeapSnapshot_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_readHeapSnapshot_01.js new file mode 100644 index 000000000..0d0d58bef --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_readHeapSnapshot_01.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can read heap snapshots. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_01.js new file mode 100644 index 000000000..6f22cbad3 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_01.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take diffs between censuses. + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "objectClass", + then: { by: "count", count: true, bytes: false }, + other: { by: "count", count: true, bytes: false }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const markers = [allocationMarker()]; + + const firstSnapshotFilePath = saveNewHeapSnapshot(); + + // Allocate and hold an additional AllocationMarker object so we can see it in + // the next heap snapshot. + markers.push(allocationMarker()); + + const secondSnapshotFilePath = saveNewHeapSnapshot(); + + yield client.readHeapSnapshot(firstSnapshotFilePath); + yield client.readHeapSnapshot(secondSnapshotFilePath); + ok(true, "Should have read both heap snapshot files"); + + const { delta } = yield client.takeCensusDiff(firstSnapshotFilePath, + secondSnapshotFilePath, + { breakdown: BREAKDOWN }); + + equal(delta.AllocationMarker.count, 1, + "There exists one new AllocationMarker in the second heap snapshot"); + + const { delta: deltaTreeNode } = yield client.takeCensusDiff(firstSnapshotFilePath, + secondSnapshotFilePath, + { breakdown: BREAKDOWN }, + { asTreeNode: true }); + + // Have to manually set these because symbol properties aren't structured + // cloned. + delta[CensusUtils.basisTotalBytes] = deltaTreeNode.totalBytes; + delta[CensusUtils.basisTotalCount] = deltaTreeNode.totalCount; + + compareCensusViewData(BREAKDOWN, delta, deltaTreeNode, + "Returning delta-census as a tree node represents same data as the report"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js new file mode 100644 index 000000000..f1ba9ce84 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take diffs between censuses as +// inverted trees. + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +add_task(function* () { + const firstSnapshotFilePath = saveNewHeapSnapshot(); + const secondSnapshotFilePath = saveNewHeapSnapshot(); + + const client = new HeapAnalysesClient(); + yield client.readHeapSnapshot(firstSnapshotFilePath); + yield client.readHeapSnapshot(secondSnapshotFilePath); + + ok(true, "Should have read both heap snapshot files"); + + const { delta } = yield client.takeCensusDiff(firstSnapshotFilePath, + secondSnapshotFilePath, + { breakdown: BREAKDOWN }); + + const { delta: deltaTreeNode } = yield client.takeCensusDiff(firstSnapshotFilePath, + secondSnapshotFilePath, + { breakdown: BREAKDOWN }, + { asInvertedTreeNode: true }); + + // Have to manually set these because symbol properties aren't structured + // cloned. + delta[CensusUtils.basisTotalBytes] = deltaTreeNode.totalBytes; + delta[CensusUtils.basisTotalCount] = deltaTreeNode.totalCount; + + compareCensusViewData(BREAKDOWN, delta, deltaTreeNode, { invert: true }); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_01.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_01.js new file mode 100644 index 000000000..e26981db4 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_01.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take censuses. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const { report } = yield client.takeCensus(snapshotFilePath); + ok(report, "Should get a report"); + equal(typeof report, "object", "report should be an object"); + + ok(report.objects); + ok(report.scripts); + ok(report.strings); + ok(report.other); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_02.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_02.js new file mode 100644 index 000000000..34494af70 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_02.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take censuses with breakdown +// options. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const { report } = yield client.takeCensus(snapshotFilePath, { + breakdown: { by: "count", count: true, bytes: true } + }); + + ok(report, "Should get a report"); + equal(typeof report, "object", "report should be an object"); + + ok(report.count); + ok(report.bytes); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_03.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_03.js new file mode 100644 index 000000000..486e250b5 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_03.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} bubbles errors properly when things +// go wrong. + +function run_test() { + run_next_test(); +} + +add_task(function* () { + const client = new HeapAnalysesClient(); + + // Snapshot file path to a file that doesn't exist. + let failed = false; + try { + yield client.readHeapSnapshot(getFilePath("foo-bar-baz" + Math.random(), true)); + } catch (e) { + failed = true; + } + ok(failed, "should not read heap snapshots that do not exist"); + + // Snapshot file path to a file that is not a heap snapshot. + failed = false; + try { + yield client.readHeapSnapshot(getFilePath("test_HeapAnalyses_takeCensus_03.js")); + } catch (e) { + failed = true; + } + ok(failed, "should not be able to read a file that is not a heap snapshot as a heap snapshot"); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + // Bad census breakdown options. + failed = false; + try { + yield client.takeCensus(snapshotFilePath, { + breakdown: { by: "some classification that we do not have" } + }); + } catch (e) { + failed = true; + } + ok(failed, "should not be able to breakdown by an unknown classification"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_04.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_04.js new file mode 100644 index 000000000..769a2d99d --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_04.js @@ -0,0 +1,118 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can send SavedFrame stacks from +// by-allocation-stack reports from the worker. + +function run_test() { + run_next_test(); +} + +add_task(function* test() { + const client = new HeapAnalysesClient(); + + // Track some allocation stacks. + + const g = newGlobal(); + const dbg = new Debugger(g); + g.eval(` // 1 + this.log = []; // 2 + function f() { this.log.push(allocationMarker()); } // 3 + function g() { this.log.push(allocationMarker()); } // 4 + function h() { this.log.push(allocationMarker()); } // 5 + `); // 6 + + // Create one allocationMarker with tracking turned off, + // so it will have no associated stack. + g.f(); + + dbg.memory.allocationSamplingProbability = 1; + + for (let [func, n] of [ [g.f, 20], + [g.g, 10], + [g.h, 5] ]) { + for (let i = 0; i < n; i++) { + dbg.memory.trackingAllocationSites = true; + // All allocations of allocationMarker occur with this line as the oldest + // stack frame. + func(); + dbg.memory.trackingAllocationSites = false; + } + } + + // Take a heap snapshot. + + const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg }); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + // Run a census broken down by class name -> allocation stack so we can grab + // only the AllocationMarker objects we have complete control over. + + const { report } = yield client.takeCensus(snapshotFilePath, { + breakdown: { by: "objectClass", + then: { by: "allocationStack", + then: { by: "count", + bytes: true, + count: true + }, + noStack: { by: "count", + bytes: true, + count: true + } + } + } + }); + + // Test the generated report. + + ok(report, "Should get a report"); + + const map = report.AllocationMarker; + ok(map, "Should get AllocationMarkers in the report."); + // From a module with a different global, and therefore a different Map + // constructor, so we can't use instanceof. + equal(map.__proto__.constructor.name, "Map"); + + equal(map.size, 4, "Should have 4 allocation stacks (including the lack of a stack)"); + + // Gather the stacks we are expecting to appear as keys, and + // check that there are no unexpected keys. + let stacks = {}; + + map.forEach((v, k) => { + if (k === "noStack") { + // No need to save this key. + } else if (k.functionDisplayName === "f" && + k.parent.functionDisplayName === "test") { + stacks.f = k; + } else if (k.functionDisplayName === "g" && + k.parent.functionDisplayName === "test") { + stacks.g = k; + } else if (k.functionDisplayName === "h" && + k.parent.functionDisplayName === "test") { + stacks.h = k; + } else { + dumpn("Unexpected allocation stack:"); + k.toString().split(/\n/g).forEach(s => dumpn(s)); + ok(false); + } + }); + + ok(map.get("noStack")); + equal(map.get("noStack").count, 1); + + ok(stacks.f); + ok(map.get(stacks.f)); + equal(map.get(stacks.f).count, 20); + + ok(stacks.g); + ok(map.get(stacks.g)); + equal(map.get(stacks.g).count, 10); + + ok(stacks.h); + ok(map.get(stacks.h)); + equal(map.get(stacks.h).count, 5); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_05.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_05.js new file mode 100644 index 000000000..7e16d9f00 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_05.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take censuses and return +// a CensusTreeNode. + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "internalType", + then: { by: "count", count: true, bytes: true } +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const { report } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }); + + const { report: treeNode } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }, { + asTreeNode: true + }); + + ok(treeNode.children.length > 0, "treeNode has children"); + ok(treeNode.children.every(type => { + return "name" in type && + "bytes" in type && + "count" in type; + }), "all of tree node's children have name, bytes, count"); + + compareCensusViewData(BREAKDOWN, report, treeNode, + "Returning census as a tree node represents same data as the report"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_06.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_06.js new file mode 100644 index 000000000..7795a9700 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_06.js @@ -0,0 +1,109 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take censuses by +// "allocationStack" and return a CensusTreeNode. + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "objectClass", + then: { + by: "allocationStack", + then: { by: "count", count: true, bytes: true }, + noStack: { by: "count", count: true, bytes: true } + }, + other: { by: "count", count: true, bytes: true } +}; + +add_task(function* () { + const g = newGlobal(); + const dbg = new Debugger(g); + + // 5 allocation markers with no stack. + g.eval(` + this.markers = []; + for (var i = 0; i < 5; i++) { + markers.push(allocationMarker()); + } + `); + + dbg.memory.allocationSamplingProbability = 1; + dbg.memory.trackingAllocationSites = true; + + // 5 allocation markers at 5 stacks. + g.eval(` + (function shouldHaveCountOfOne() { + markers.push(allocationMarker()); + markers.push(allocationMarker()); + markers.push(allocationMarker()); + markers.push(allocationMarker()); + markers.push(allocationMarker()); + }()); + `); + + // 5 allocation markers at 1 stack. + g.eval(` + (function shouldHaveCountOfFive() { + for (var i = 0; i < 5; i++) { + markers.push(allocationMarker()); + } + }()); + `); + + const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg }); + + const client = new HeapAnalysesClient(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const { report } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }); + + const { report: treeNode } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }, { + asTreeNode: true + }); + + const markers = treeNode.children.find(c => c.name === "AllocationMarker"); + ok(markers); + + const noStack = markers.children.find(c => c.name === "noStack"); + equal(noStack.count, 5); + + let numShouldHaveFiveFound = 0; + let numShouldHaveOneFound = 0; + + function walk(node) { + if (node.children) { + node.children.forEach(walk); + } + + if (!isSavedFrame(node.name)) { + return; + } + + if (node.name.functionDisplayName === "shouldHaveCountOfFive") { + equal(node.count, 5, "shouldHaveCountOfFive should have count of five"); + numShouldHaveFiveFound++; + } + + if (node.name.functionDisplayName === "shouldHaveCountOfOne") { + equal(node.count, 1, "shouldHaveCountOfOne should have count of one"); + numShouldHaveOneFound++; + } + } + markers.children.forEach(walk); + + equal(numShouldHaveFiveFound, 1); + equal(numShouldHaveOneFound, 5); + + compareCensusViewData(BREAKDOWN, report, treeNode, + "Returning census as a tree node represents same data as the report"); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js new file mode 100644 index 000000000..986b3aaa8 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js @@ -0,0 +1,52 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the HeapAnalyses{Client,Worker} can take censuses and return +// an inverted CensusTreeNode. + +function run_test() { + run_next_test(); +} + +const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, +}; + +add_task(function* () { + const client = new HeapAnalysesClient(); + + const snapshotFilePath = saveNewHeapSnapshot(); + yield client.readHeapSnapshot(snapshotFilePath); + ok(true, "Should have read the heap snapshot"); + + const { report } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }); + + const { report: treeNode } = yield client.takeCensus(snapshotFilePath, { + breakdown: BREAKDOWN + }, { + asInvertedTreeNode: true + }); + + compareCensusViewData(BREAKDOWN, report, treeNode, { invert: true }); + + client.destroy(); +}); diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_01.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_01.js new file mode 100644 index 000000000..2ec577bd0 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_01.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Sanity test that we can compute shortest paths. +// +// Because the actual heap graph is too unpredictable and likely to drastically +// change as various implementation bits change, we don't test exact paths +// here. See js/src/jsapi-tests/testUbiNode.cpp for such tests, where we can +// control the specific graph shape and structure and so testing exact paths is +// reliable. + +function run_test() { + const path = ChromeUtils.saveHeapSnapshot({ runtime: true }); + const snapshot = ChromeUtils.readHeapSnapshot(path); + + const dominatorTree = snapshot.computeDominatorTree(); + const dominatedByRoot = dominatorTree.getImmediatelyDominated(dominatorTree.root) + .slice(0, 10); + ok(dominatedByRoot); + ok(dominatedByRoot.length); + + const targetSet = new Set(dominatedByRoot); + + const shortestPaths = snapshot.computeShortestPaths(dominatorTree.root, dominatedByRoot, 2); + ok(shortestPaths); + ok(shortestPaths instanceof Map); + ok(shortestPaths.size === targetSet.size); + + for (let [target, paths] of shortestPaths) { + ok(targetSet.has(target), + "We should only get paths for our targets"); + targetSet.delete(target); + + ok(paths.length > 0, + "We must have at least one path, since the target is dominated by the root"); + ok(paths.length <= 2, + "Should not have recorded more paths than the max requested"); + + dumpn("---------------------"); + dumpn("Shortest paths for 0x" + target.toString(16) + ":"); + for (let path of paths) { + dumpn(" path ="); + for (let part of path) { + dumpn(" predecessor: 0x" + part.predecessor.toString(16) + + "; edge: " + part.edge); + } + } + dumpn("---------------------"); + + for (let path of paths) { + ok(path.length > 0, "Cannot have zero length paths"); + ok(path[0].predecessor === dominatorTree.root, + "The first predecessor is always our start node"); + + for (let part of path) { + ok(part.predecessor, "Each part of a path has a predecessor"); + ok(!!snapshot.describeNode({ by: "count", count: true, bytes: true}, + part.predecessor), + "The predecessor is in the heap snapshot"); + ok("edge" in part, "Each part has an (potentially null) edge property"); + } + } + } + + ok(targetSet.size === 0, + "We found paths for all of our targets"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_02.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_02.js new file mode 100644 index 000000000..04fe58733 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_computeShortestPaths_02.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test computing shortest paths with invalid arguments. + +function run_test() { + const path = ChromeUtils.saveHeapSnapshot({ runtime: true }); + const snapshot = ChromeUtils.readHeapSnapshot(path); + + const dominatorTree = snapshot.computeDominatorTree(); + const target = dominatorTree.getImmediatelyDominated(dominatorTree.root).pop(); + ok(target); + + let threw = false; + try { + snapshot.computeShortestPaths(0, [target], 2); + } catch (_) { + threw = true; + } + ok(threw, "invalid start node should throw"); + + threw = false; + try { + snapshot.computeShortestPaths(dominatorTree.root, [0], 2); + } catch (_) { + threw = true; + } + ok(threw, "invalid target nodes should throw"); + + threw = false; + try { + snapshot.computeShortestPaths(dominatorTree.root, [], 2); + } catch (_) { + threw = true; + } + ok(threw, "empty target nodes should throw"); + + threw = false; + try { + snapshot.computeShortestPaths(dominatorTree.root, [target], 0); + } catch (_) { + threw = true; + } + ok(threw, "0 max paths should throw"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_creationTime_01.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_creationTime_01.js new file mode 100644 index 000000000..0d08fea16 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_creationTime_01.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.creationTime returns the expected time. + +function waitForThirtyMilliseconds() { + const start = Date.now(); + while (Date.now() - start < 30) ; +} + +function run_test() { + const start = Date.now() * 1000; + do_print("start = " + start); + + // Because Date.now() is less precise than the snapshot's time stamp, give it + // a little bit of head room. Additionally, WinXP's timer only has granularity + // of +/- 15ms. + waitForThirtyMilliseconds(); + const path = ChromeUtils.saveHeapSnapshot({ runtime: true }); + waitForThirtyMilliseconds(); + + const end = Date.now() * 1000; + do_print("end = " + end); + + const snapshot = ChromeUtils.readHeapSnapshot(path); + do_print("snapshot.creationTime = " + snapshot.creationTime); + + ok(snapshot.creationTime >= start); + ok(snapshot.creationTime <= end); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_deepStack_01.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_deepStack_01.js new file mode 100644 index 000000000..9eb11d9af --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_deepStack_01.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can save a core dump with very deep allocation stacks and read +// it back into a HeapSnapshot. + +function stackDepth(stack) { + return stack ? 1 + stackDepth(stack.parent) : 0; +} + +function run_test() { + // Create a Debugger observing a debuggee's allocations. + const debuggee = new Cu.Sandbox(null); + const dbg = new Debugger(debuggee); + dbg.memory.trackingAllocationSites = true; + + // Allocate some objects in the debuggee that will have their allocation + // stacks recorded by the Debugger. + + debuggee.eval("this.objects = []"); + debuggee.eval( + (function recursiveAllocate(n) { + if (n <= 0) + return; + + // Make sure to recurse before pushing the object so that when TCO is + // implemented sometime in the future, it doesn't invalidate this test. + recursiveAllocate(n - 1); + this.objects.push({}); + }).toString() + ); + debuggee.eval("recursiveAllocate = recursiveAllocate.bind(this);"); + debuggee.eval("recursiveAllocate(200);"); + + // Now save a snapshot that will include the allocation stacks and read it + // back again. + + const filePath = ChromeUtils.saveHeapSnapshot({ runtime: true }); + ok(true, "Should be able to save a snapshot."); + + const snapshot = ChromeUtils.readHeapSnapshot(filePath); + ok(snapshot, "Should be able to read a heap snapshot"); + ok(snapshot instanceof HeapSnapshot, "Should be an instanceof HeapSnapshot"); + + const report = snapshot.takeCensus({ + breakdown: { by: "allocationStack", + then: { by: "count", bytes: true, count: true }, + noStack: { by: "count", bytes: true, count: true } + } + }); + + // Keep this synchronized with `HeapSnapshot::MAX_STACK_DEPTH`! + const MAX_STACK_DEPTH = 60; + + let foundStacks = false; + report.forEach((v, k) => { + if (k === "noStack") { + return; + } + + foundStacks = true; + const depth = stackDepth(k); + dumpn("Stack depth is " + depth); + ok(depth <= MAX_STACK_DEPTH, + "Every stack should have depth less than or equal to the maximum stack depth"); + }); + ok(foundStacks); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_describeNode_01.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_describeNode_01.js new file mode 100644 index 000000000..d79cb5a7b --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_describeNode_01.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can describe nodes with a breakdown. + +function run_test() { + const path = saveNewHeapSnapshot(); + const snapshot = ChromeUtils.readHeapSnapshot(path); + ok(snapshot.describeNode); + equal(typeof snapshot.describeNode, "function"); + + const dt = snapshot.computeDominatorTree(); + + let threw = false; + try { + snapshot.describeNode(undefined, dt.root); + } catch (_) { + threw = true; + } + ok(threw, "Should require a breakdown"); + + const breakdown = { + by: "coarseType", + objects: { by: "objectClass" }, + scripts: { by: "internalType" }, + strings: { by: "internalType" }, + other: { by: "internalType" } + }; + + threw = false; + try { + snapshot.describeNode(breakdown, 0); + } catch (_) { + threw = true; + } + ok(threw, "Should throw when given an invalid node id"); + + const description = snapshot.describeNode(breakdown, dt.root); + ok(description); + ok(description.other); + ok(description.other["JS::ubi::RootList"]); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_01.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_01.js new file mode 100644 index 000000000..f3b3090b0 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_01.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus returns a value of an appropriate +// shape. Ported from js/src/jit-tests/debug/Memory-takeCensus-01.js + +function run_test() { + var dbg = new Debugger; + + function checkProperties(census) { + equal(typeof census, "object"); + for (prop of Object.getOwnPropertyNames(census)) { + var desc = Object.getOwnPropertyDescriptor(census, prop); + equal(desc.enumerable, true); + equal(desc.configurable, true); + equal(desc.writable, true); + if (typeof desc.value === "object") + checkProperties(desc.value); + else + equal(typeof desc.value, "number"); + } + } + + checkProperties(saveHeapSnapshotAndTakeCensus(dbg)); + + var g = newGlobal(); + dbg.addDebuggee(g); + checkProperties(saveHeapSnapshotAndTakeCensus(dbg)); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_02.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_02.js new file mode 100644 index 000000000..680ac9b58 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_02.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus behaves plausibly as we allocate objects. +// +// Exact object counts vary in ways we can't predict. For example, +// BaselineScripts can hold onto "template objects", which exist only to hold +// the shape and type for newly created objects. When BaselineScripts are +// discarded, these template objects go with them. +// +// So instead of expecting precise counts, we expect counts that are at least as +// many as we would expect given the object graph we've built. +// +// Ported from js/src/jit-tests/debug/Memory-takeCensus-02.js + +function run_test() { + // A Debugger with no debuggees had better not find anything. + var dbg = new Debugger; + var census0 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census0, "census0", Census.assertAllZeros); + + function newGlobalWithDefs() { + var g = newGlobal(); + g.eval(` + function times(n, fn) { + var a=[]; + for (var i = 0; i<n; i++) + a.push(fn()); + return a; + } + `); + return g; + } + + // Allocate a large number of various types of objects, and check that census + // finds them. + var g = newGlobalWithDefs(); + dbg.addDebuggee(g); + + g.eval("var objs = times(100, () => ({}));"); + g.eval("var rxs = times(200, () => /foo/);"); + g.eval("var ars = times(400, () => []);"); + g.eval("var fns = times(800, () => () => {});"); + + var census1 = dbg.memory.takeCensus(dbg); + Census.walkCensus(census1, "census1", + Census.assertAllNotLessThan( + { "objects": + { "Object": { count: 100 }, + "RegExp": { count: 200 }, + "Array": { count: 400 }, + "Function": { count: 800 } + } + })); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_03.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_03.js new file mode 100644 index 000000000..25f2c3791 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_03.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus behaves plausibly as we add and remove +// debuggees. +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-03.js + +function run_test() { + var dbg = new Debugger; + + var census0 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census0, "census0", Census.assertAllZeros); + + var g1 = newGlobal(); + dbg.addDebuggee(g1); + var census1 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census1, "census1", Census.assertAllNotLessThan(census0)); + + var g2 = newGlobal(); + dbg.addDebuggee(g2); + var census2 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census2, "census2", Census.assertAllNotLessThan(census1)); + + dbg.removeDebuggee(g2); + var census3 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census3, "census3", Census.assertAllEqual(census1)); + + dbg.removeDebuggee(g1); + var census4 = saveHeapSnapshotAndTakeCensus(dbg); + Census.walkCensus(census4, "census4", Census.assertAllEqual(census0)); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_04.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_04.js new file mode 100644 index 000000000..799844cde --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_04.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that HeapSnapshot.prototype.takeCensus finds GC roots that are on the +// stack. +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-04.js + +function run_test() { + var g = newGlobal(); + var dbg = new Debugger(g); + + g.eval(` +function withAllocationMarkerOnStack(f) { + (function () { + var onStack = allocationMarker(); + f(); + }()); +} +`); + + equal("AllocationMarker" in saveHeapSnapshotAndTakeCensus(dbg).objects, false, + "There shouldn't exist any allocation markers in the census."); + + var allocationMarkerCount; + g.withAllocationMarkerOnStack(() => { + const census = saveHeapSnapshotAndTakeCensus(dbg); + allocationMarkerCount = census.objects.AllocationMarker.count; + }); + + equal(allocationMarkerCount, 1, + "Should have one allocation marker in the census, because there " + + "was one on the stack."); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_05.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_05.js new file mode 100644 index 000000000..da6067624 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_05.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that HeapSnapshot.prototype.takeCensus finds cross compartment +// wrapper GC roots. +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-05.js + +function run_test() { + var g = newGlobal(); + var dbg = new Debugger(g); + + equal("AllocationMarker" in saveHeapSnapshotAndTakeCensus(dbg).objects, false, + "No allocation markers should exist in the census."); + + this.ccw = g.allocationMarker(); + + const census = saveHeapSnapshotAndTakeCensus(dbg); + equal(census.objects.AllocationMarker.count, 1, + "Should have one allocation marker in the census, because there " + + "is one cross-compartment wrapper referring to it."); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_06.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_06.js new file mode 100644 index 000000000..0412410c0 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_06.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check HeapSnapshot.prototype.takeCensus handling of 'breakdown' argument. +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-06.js + +function run_test() { + var Pattern = Match.Pattern; + + var g = newGlobal(); + var dbg = new Debugger(g); + + Pattern({ count: Pattern.NATURAL, + bytes: Pattern.NATURAL }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count" } })); + + let census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count", count: false, bytes: false } }); + equal("count" in census, false); + equal("bytes" in census, false); + + census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count", count: true, bytes: false } }); + equal("count" in census, true); + equal("bytes" in census, false); + + census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count", count: false, bytes: true } }); + equal("count" in census, false); + equal("bytes" in census, true); + + census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count", count: true, bytes: true } }); + equal("count" in census, true); + equal("bytes" in census, true); + + + // Pattern doesn't mind objects with extra properties, so we'll restrict this + // list to the object classes we're pretty sure are going to stick around for + // the forseeable future. + Pattern({ + Function: { count: Pattern.NATURAL }, + Object: { count: Pattern.NATURAL }, + Debugger: { count: Pattern.NATURAL }, + Sandbox: { count: Pattern.NATURAL }, + + // The below are all Debugger prototype objects. + Source: { count: Pattern.NATURAL }, + Environment: { count: Pattern.NATURAL }, + Script: { count: Pattern.NATURAL }, + Memory: { count: Pattern.NATURAL }, + Frame: { count: Pattern.NATURAL } + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "objectClass" } })); + + Pattern({ + objects: { count: Pattern.NATURAL }, + scripts: { count: Pattern.NATURAL }, + strings: { count: Pattern.NATURAL }, + other: { count: Pattern.NATURAL } + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "coarseType" } })); + + // As for { by: 'objectClass' }, restrict our pattern to the types + // we predict will stick around for a long time. + Pattern({ + JSString: { count: Pattern.NATURAL }, + "js::Shape": { count: Pattern.NATURAL }, + JSObject: { count: Pattern.NATURAL }, + JSScript: { count: Pattern.NATURAL } + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "internalType" } })); + + + // Nested breakdowns. + + let coarseTypePattern = { + objects: { count: Pattern.NATURAL }, + scripts: { count: Pattern.NATURAL }, + strings: { count: Pattern.NATURAL }, + other: { count: Pattern.NATURAL } + }; + + Pattern({ + JSString: coarseTypePattern, + "js::Shape": coarseTypePattern, + JSObject: coarseTypePattern, + JSScript: coarseTypePattern, + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "internalType", + then: { by: "coarseType" } + } + })); + + Pattern({ + Function: { count: Pattern.NATURAL }, + Object: { count: Pattern.NATURAL }, + Debugger: { count: Pattern.NATURAL }, + Sandbox: { count: Pattern.NATURAL }, + other: coarseTypePattern + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { + by: "objectClass", + then: { by: "count" }, + other: { by: "coarseType" } + } + })); + + Pattern({ + objects: { count: Pattern.NATURAL, label: "object" }, + scripts: { count: Pattern.NATURAL, label: "scripts" }, + strings: { count: Pattern.NATURAL, label: "strings" }, + other: { count: Pattern.NATURAL, label: "other" } + }) + .assert(saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { + by: "coarseType", + objects: { by: "count", label: "object" }, + scripts: { by: "count", label: "scripts" }, + strings: { by: "count", label: "strings" }, + other: { by: "count", label: "other" } + } + })); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_07.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_07.js new file mode 100644 index 000000000..f5c36056f --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_07.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus breakdown: check error handling on property +// gets. +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-07.js + +function run_test() { + var g = newGlobal(); + var dbg = new Debugger(g); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { get by() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "count", get count() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "count", get bytes() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "objectClass", get then() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "objectClass", get other() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "coarseType", get objects() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "coarseType", get scripts() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "coarseType", get strings() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "coarseType", get other() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + + + assertThrowsValue(() => { + saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "internalType", get then() { throw "ಠ_ಠ"; } } + }); + }, "ಠ_ಠ"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_08.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_08.js new file mode 100644 index 000000000..5934aa919 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_08.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus: test by: 'count' breakdown +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-08.js + +function run_test() { + let g = newGlobal(); + let dbg = new Debugger(g); + + g.eval(` + var stuff = []; + function add(n, c) { + for (let i = 0; i < n; i++) + stuff.push(c()); + } + + let count = 0; + + function obj() { return { count: count++ }; } + obj.factor = 1; + + // This creates a closure (a function JSObject) that has captured + // a Call object. So each call creates two items. + function fun() { let v = count; return () => { return v; } } + fun.factor = 2; + + function str() { return 'perambulator' + count++; } + str.factor = 1; + + // Eval a fresh text each time, allocating: + // - a fresh ScriptSourceObject + // - a new JSScripts, not an eval cache hits + // - a fresh prototype object + // - a fresh Call object, since the eval makes 'ev' heavyweight + // - the new function itself + function ev() { + return eval(\`(function () { return \${ count++ } })\`); + } + ev.factor = 5; + + // A new object (1) with a new shape (2) with a new atom (3) + function shape() { return { [ 'theobroma' + count++ ]: count }; } + shape.factor = 3; + `); + + let baseline = 0; + function countIncreasedByAtLeast(n) { + let oldBaseline = baseline; + + // Since a census counts only reachable objects, one might assume that calling + // GC here would have no effect on the census results. But GC also throws away + // JIT code and any objects it might be holding (template objects, say); + // takeCensus reaches those. Shake everything loose that we can, to make the + // census approximate reachability a bit more closely, and make our results a + // bit more predictable. + gc(g, "shrinking"); + + baseline = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "count" } }).count; + return baseline >= oldBaseline + n; + } + + countIncreasedByAtLeast(0); + + g.add(100, g.obj); + ok(countIncreasedByAtLeast(g.obj.factor * 100)); + + g.add(100, g.fun); + ok(countIncreasedByAtLeast(g.fun.factor * 100)); + + g.add(100, g.str); + ok(countIncreasedByAtLeast(g.str.factor * 100)); + + g.add(100, g.ev); + ok(countIncreasedByAtLeast(g.ev.factor * 100)); + + g.add(100, g.shape); + ok(countIncreasedByAtLeast(g.shape.factor * 100)); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_09.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_09.js new file mode 100644 index 000000000..bbacccc8d --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_09.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// HeapSnapshot.prototype.takeCensus: by: allocationStack breakdown +// +// Ported from js/src/jit-test/tests/debug/Memory-takeCensus-09.js + +function run_test() { + var g = newGlobal(); + var dbg = new Debugger(g); + + g.eval(` // 1 + var log = []; // 2 + function f() { log.push(allocationMarker()); } // 3 + function g() { f(); } // 4 + function h() { f(); } // 5 + `); // 6 + + // Create one allocationMarker with tracking turned off, + // so it will have no associated stack. + g.f(); + + dbg.memory.allocationSamplingProbability = 1; + + for ([func, n] of [[g.f, 20], [g.g, 10], [g.h, 5]]) { + for (let i = 0; i < n; i++) { + dbg.memory.trackingAllocationSites = true; + // All allocations of allocationMarker occur with this line as the oldest + // stack frame. + func(); + dbg.memory.trackingAllocationSites = false; + } + } + + let census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "objectClass", + then: { by: "allocationStack", + then: { by: "count", + label: "haz stack" + }, + noStack: { by: "count", + label: "no haz stack" + } + } + } + }); + + let map = census.AllocationMarker; + ok(map instanceof Map, "Should be a Map instance"); + equal(map.size, 4, "Should have 4 allocation stacks (including the lack of a stack)"); + + // Gather the stacks we are expecting to appear as keys, and + // check that there are no unexpected keys. + let stacks = { }; + + map.forEach((v, k) => { + if (k === "noStack") { + // No need to save this key. + } else if (k.functionDisplayName === "f" && + k.parent.functionDisplayName === "run_test") { + stacks.f = k; + } else if (k.functionDisplayName === "f" && + k.parent.functionDisplayName === "g" && + k.parent.parent.functionDisplayName === "run_test") { + stacks.fg = k; + } else if (k.functionDisplayName === "f" && + k.parent.functionDisplayName === "h" && + k.parent.parent.functionDisplayName === "run_test") { + stacks.fh = k; + } else { + dumpn("Unexpected allocation stack:"); + k.toString().split(/\n/g).forEach(s => dumpn(s)); + ok(false); + } + }); + + equal(map.get("noStack").label, "no haz stack"); + equal(map.get("noStack").count, 1); + + ok(stacks.f); + equal(map.get(stacks.f).label, "haz stack"); + equal(map.get(stacks.f).count, 20); + + ok(stacks.fg); + equal(map.get(stacks.fg).label, "haz stack"); + equal(map.get(stacks.fg).count, 10); + + ok(stacks.fh); + equal(map.get(stacks.fh).label, "haz stack"); + equal(map.get(stacks.fh).count, 5); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_10.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_10.js new file mode 100644 index 000000000..a7f987f5a --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_10.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check byte counts produced by takeCensus. +// +// Ported from js/src/jit-test/tests/debug/Memory-take Census-10.js + +function run_test() { + let g = newGlobal(); + let dbg = new Debugger(g); + + let sizeOfAM = byteSize(allocationMarker()); + + // Allocate a single allocation marker, and check that we can find it. + g.eval("var hold = allocationMarker();"); + let census = saveHeapSnapshotAndTakeCensus(dbg, { breakdown: { by: "objectClass" } }); + equal(census.AllocationMarker.count, 1); + equal(census.AllocationMarker.bytes, sizeOfAM); + g.hold = null; + + g.eval(` // 1 + var objs = []; // 2 + function fnerd() { // 3 + objs.push(allocationMarker()); // 4 + for (let i = 0; i < 10; i++) // 5 + objs.push(allocationMarker()); // 6 + } // 7 + `); // 8 + + dbg.memory.allocationSamplingProbability = 1; + dbg.memory.trackingAllocationSites = true; + g.fnerd(); + dbg.memory.trackingAllocationSites = false; + + census = saveHeapSnapshotAndTakeCensus(dbg, { + breakdown: { by: "objectClass", + then: { by: "allocationStack" } + } + }); + + let seen = 0; + census.AllocationMarker.forEach((v, k) => { + equal(k.functionDisplayName, "fnerd"); + switch (k.line) { + case 4: + equal(v.count, 1); + equal(v.bytes, sizeOfAM); + seen++; + break; + + case 6: + equal(v.count, 10); + equal(v.bytes, 10 * sizeOfAM); + seen++; + break; + + default: + dumpn("Unexpected stack:"); + k.toString().split(/\n/g).forEach(s => dumpn(s)); + ok(false); + break; + } + }); + + equal(seen, 2); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_11.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_11.js new file mode 100644 index 000000000..3d898b2d1 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_11.js @@ -0,0 +1,116 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that Debugger.Memory.prototype.takeCensus and +// HeapSnapshot.prototype.takeCensus return the same data for the same heap +// graph. + +function doLiveAndOfflineCensus(g, dbg, opts) { + dbg.memory.allocationSamplingProbability = 1; + dbg.memory.trackingAllocationSites = true; + g.eval(` // 1 + (function unsafeAtAnySpeed() { // 2 + for (var i = 0; i < 100; i++) { // 3 + this.markers.push(allocationMarker()); // 4 + } // 5 + }()); // 6 + `); // 7 + dbg.memory.trackingAllocationSites = false; + + return { + live: dbg.memory.takeCensus(opts), + offline: saveHeapSnapshotAndTakeCensus(dbg, opts) + }; +} + +function run_test() { + var g = newGlobal(); + var dbg = new Debugger(g); + + g.eval("this.markers = []"); + const markerSize = byteSize(allocationMarker()); + + // First, test that we get the same counts and sizes as we allocate and retain + // more things. + + let prevCount = 0; + let prevBytes = 0; + + for (var i = 0; i < 10; i++) { + const { live, offline } = doLiveAndOfflineCensus(g, dbg, { + breakdown: { by: "objectClass", + then: { by: "count"} } + }); + + equal(live.AllocationMarker.count, offline.AllocationMarker.count); + equal(live.AllocationMarker.bytes, offline.AllocationMarker.bytes); + equal(live.AllocationMarker.count, prevCount + 100); + equal(live.AllocationMarker.bytes, prevBytes + 100 * markerSize); + + prevCount = live.AllocationMarker.count; + prevBytes = live.AllocationMarker.bytes; + } + + // Second, test that the reported allocation stacks and counts and sizes at + // those allocation stacks match up. + + const { live, offline } = doLiveAndOfflineCensus(g, dbg, { + breakdown: { by: "objectClass", + then: { by: "allocationStack"} } + }); + + equal(live.AllocationMarker.size, offline.AllocationMarker.size); + // One stack with the loop further above, and another stack featuring the call + // right above. + equal(live.AllocationMarker.size, 2); + + // Note that because SavedFrame stacks reconstructed from an offline heap + // snapshot don't have the same principals as SavedFrame stacks captured from + // a live stack, the live and offline allocation stacks won't be identity + // equal, but should be structurally the same. + + const liveEntries = []; + live.AllocationMarker.forEach((v, k) => { + dumpn("Allocation stack:"); + k.toString().split(/\n/g).forEach(s => dumpn(s)); + + equal(k.functionDisplayName, "unsafeAtAnySpeed"); + equal(k.line, 4); + + liveEntries.push([k.toString(), v]); + }); + + const offlineEntries = []; + offline.AllocationMarker.forEach((v, k) => { + dumpn("Allocation stack:"); + k.toString().split(/\n/g).forEach(s => dumpn(s)); + + equal(k.functionDisplayName, "unsafeAtAnySpeed"); + equal(k.line, 4); + + offlineEntries.push([k.toString(), v]); + }); + + const sortEntries = (a, b) => { + if (a[0] < b[0]) { + return -1; + } else if (a[0] > b[0]) { + return 1; + } else { + return 0; + } + }; + liveEntries.sort(sortEntries); + offlineEntries.sort(sortEntries); + + equal(liveEntries.length, live.AllocationMarker.size); + equal(liveEntries.length, offlineEntries.length); + + for (let i = 0; i < liveEntries.length; i++) { + equal(liveEntries[i][0], offlineEntries[i][0]); + equal(liveEntries[i][1].count, offlineEntries[i][1].count); + equal(liveEntries[i][1].bytes, offlineEntries[i][1].bytes); + } + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_12.js b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_12.js new file mode 100644 index 000000000..f10dd5b03 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_HeapSnapshot_takeCensus_12.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that when we take a census and get a bucket list of ids that matched the +// given category, that the returned ids are all in the snapshot and their +// reported category. + +function run_test() { + const g = newGlobal(); + const dbg = new Debugger(g); + + const path = saveNewHeapSnapshot({ debugger: dbg }); + const snapshot = readHeapSnapshot(path); + + const bucket = { by: "bucket" }; + const count = { by: "count", count: true, bytes: false }; + const objectClassCount = { by: "objectClass", then: count, other: count }; + + const byClassName = snapshot.takeCensus({ + breakdown: { + by: "objectClass", + then: bucket, + other: bucket, + } + }); + + const byClassNameCount = snapshot.takeCensus({ + breakdown: objectClassCount + }); + + const keys = new Set(Object.keys(byClassName)); + equal(keys.size, Object.keys(byClassNameCount).length, + "Should have the same number of keys."); + for (let k of Object.keys(byClassNameCount)) { + ok(keys.has(k), "Should not have any unexpected class names"); + } + + for (let key of Object.keys(byClassName)) { + equal(byClassNameCount[key].count, byClassName[key].length, + "Length of the bucket and count should be equal"); + + for (let id of byClassName[key]) { + const desc = snapshot.describeNode(objectClassCount, id); + equal(desc[key].count, 1, + "Describing the bucketed node confirms that it belongs to the category"); + } + } + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot.js b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot.js new file mode 100644 index 000000000..dde139ffd --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot.js @@ -0,0 +1,20 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can read core dumps into HeapSnapshot instances. + +if (typeof Debugger != "function") { + const { addDebuggerToGlobal } = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + addDebuggerToGlobal(this); +} + +function run_test() { + const filePath = ChromeUtils.saveHeapSnapshot({ globals: [this] }); + ok(true, "Should be able to save a snapshot."); + + const snapshot = ChromeUtils.readHeapSnapshot(filePath); + ok(snapshot, "Should be able to read a heap snapshot"); + ok(snapshot instanceof HeapSnapshot, "Should be an instanceof HeapSnapshot"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_with_allocations.js b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_with_allocations.js new file mode 100644 index 000000000..d91f36f56 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_with_allocations.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can save a core dump with allocation stacks and read it back +// into a HeapSnapshot. + +if (typeof Debugger != "function") { + const { addDebuggerToGlobal } = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + addDebuggerToGlobal(this); +} + +function run_test() { + // Create a Debugger observing a debuggee's allocations. + const debuggee = new Cu.Sandbox(null); + const dbg = new Debugger(debuggee); + dbg.memory.trackingAllocationSites = true; + + // Allocate some objects in the debuggee that will have their allocation + // stacks recorded by the Debugger. + debuggee.eval("this.objects = []"); + for (let i = 0; i < 100; i++) { + debuggee.eval("this.objects.push({})"); + } + + // Now save a snapshot that will include the allocation stacks and read it + // back again. + + const filePath = ChromeUtils.saveHeapSnapshot({ runtime: true }); + ok(true, "Should be able to save a snapshot."); + + const snapshot = ChromeUtils.readHeapSnapshot(filePath); + ok(snapshot, "Should be able to read a heap snapshot"); + ok(snapshot instanceof HeapSnapshot, "Should be an instanceof HeapSnapshot"); + + do_test_finished(); +} diff --git a/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_worker.js b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_worker.js new file mode 100644 index 000000000..76461b694 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_ReadHeapSnapshot_worker.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that we can read core dumps into HeapSnapshot instances in a worker. + +add_task(function* () { + const worker = new ChromeWorker("resource://test/heap-snapshot-worker.js"); + worker.postMessage({}); + + let assertionCount = 0; + worker.onmessage = e => { + if (e.data.type !== "assertion") { + return; + } + + ok(e.data.passed, e.data.msg + "\n" + e.data.stack); + assertionCount++; + }; + + yield waitForDone(worker); + + ok(assertionCount > 0); + worker.terminate(); +}); + +function waitForDone(w) { + return new Promise((resolve, reject) => { + w.onerror = e => { + reject(); + ok(false, "Error in worker: " + e); + }; + + w.addEventListener("message", function listener(e) { + if (e.data.type === "done") { + w.removeEventListener("message", listener, false); + resolve(); + } + }, false); + }); +} diff --git a/dom/heapsnapshot/tests/unit/test_SaveHeapSnapshot.js b/dom/heapsnapshot/tests/unit/test_SaveHeapSnapshot.js new file mode 100644 index 000000000..affd8d1e4 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_SaveHeapSnapshot.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the ChromeUtils interface. + +if (typeof Debugger != "function") { + const { addDebuggerToGlobal } = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + addDebuggerToGlobal(this); +} + +function run_test() { + ok(ChromeUtils, "Should be able to get the ChromeUtils interface"); + + testBadParameters(); + testGoodParameters(); + + do_test_finished(); +} + +function testBadParameters() { + throws(() => ChromeUtils.saveHeapSnapshot(), + "Should throw if arguments aren't passed in."); + + throws(() => ChromeUtils.saveHeapSnapshot(null), + "Should throw if boundaries isn't an object."); + + throws(() => ChromeUtils.saveHeapSnapshot({}), + "Should throw if the boundaries object doesn't have any properties."); + + throws(() => ChromeUtils.saveHeapSnapshot({ runtime: true, + globals: [this] }), + "Should throw if the boundaries object has more than one property."); + + throws(() => ChromeUtils.saveHeapSnapshot({ debugger: {} }), + "Should throw if the debuggees object is not a Debugger object"); + + throws(() => ChromeUtils.saveHeapSnapshot({ globals: [{}] }), + "Should throw if the globals array contains non-global objects."); + + throws(() => ChromeUtils.saveHeapSnapshot({ runtime: false }), + "Should throw if runtime is supplied and is not true."); + + throws(() => ChromeUtils.saveHeapSnapshot({ globals: null }), + "Should throw if globals is not an object."); + + throws(() => ChromeUtils.saveHeapSnapshot({ globals: {} }), + "Should throw if globals is not an array."); + + throws(() => ChromeUtils.saveHeapSnapshot({ debugger: Debugger.prototype }), + "Should throw if debugger is the Debugger.prototype object."); + + throws(() => ChromeUtils.saveHeapSnapshot({ get globals() { return [this]; } }), + "Should throw if boundaries property is a getter."); +} + +const makeNewSandbox = () => + Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")()); + +function testGoodParameters() { + let sandbox = makeNewSandbox(); + let dbg = new Debugger(sandbox); + + ChromeUtils.saveHeapSnapshot({ debugger: dbg }); + ok(true, "Should be able to save a snapshot for a debuggee global."); + + dbg = new Debugger; + let sandboxes = Array(10).fill(null).map(makeNewSandbox); + sandboxes.forEach(sb => dbg.addDebuggee(sb)); + + ChromeUtils.saveHeapSnapshot({ debugger: dbg }); + ok(true, "Should be able to save a snapshot for many debuggee globals."); + + dbg = new Debugger; + ChromeUtils.saveHeapSnapshot({ debugger: dbg }); + ok(true, "Should be able to save a snapshot with no debuggee globals."); + + ChromeUtils.saveHeapSnapshot({ globals: [this] }); + ok(true, "Should be able to save a snapshot for a specific global."); + + ChromeUtils.saveHeapSnapshot({ runtime: true }); + ok(true, "Should be able to save a snapshot of the full runtime."); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-01.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-01.js new file mode 100644 index 000000000..16038c5c4 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-01.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests CensusTreeNode with `internalType` breakdown. + */ + +const BREAKDOWN = { + by: "internalType", + then: { by: "count", count: true, bytes: true } +}; + +const REPORT = { + "JSObject": { + "bytes": 100, + "count": 10, + }, + "js::Shape": { + "bytes": 500, + "count": 50, + }, + "JSString": { + "bytes": 10, + "count": 1, + }, +}; + +const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 610, + count: 0, + totalCount: 61, + children: [ + { + name: "js::Shape", + bytes: 500, + totalBytes: 500, + count: 50, + totalCount: 50, + children: undefined, + id: 3, + parent: 1, + reportLeafIndex: 2, + }, + { + name: "JSObject", + bytes: 100, + totalBytes: 100, + count: 10, + totalCount: 10, + children: undefined, + id: 2, + parent: 1, + reportLeafIndex: 1, + }, + { + name: "JSString", + bytes: 10, + totalBytes: 10, + count: 1, + totalCount: 1, + children: undefined, + id: 4, + parent: 1, + reportLeafIndex: 3, + }, + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, +}; + +function run_test() { + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-02.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-02.js new file mode 100644 index 000000000..37d039954 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-02.js @@ -0,0 +1,136 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests CensusTreeNode with `coarseType` breakdown. + */ + +const countBreakdown = { by: "count", count: true, bytes: true }; + +const BREAKDOWN = { + by: "coarseType", + objects: { by: "objectClass", then: countBreakdown }, + strings: countBreakdown, + scripts: countBreakdown, + other: { by: "internalType", then: countBreakdown }, +}; + +const REPORT = { + "objects": { + "Function": { bytes: 10, count: 1 }, + "Array": { bytes: 20, count: 2 }, + }, + "strings": { bytes: 10, count: 1 }, + "scripts": { bytes: 1, count: 1 }, + "other": { + "js::Shape": { bytes: 30, count: 3 }, + "js::Shape2": { bytes: 40, count: 4 } + }, +}; + +const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 111, + count: 0, + totalCount: 12, + children: [ + { + name: "other", + count: 0, + totalCount: 7, + bytes: 0, + totalBytes: 70, + children: [ + { + name: "js::Shape2", + bytes: 40, + totalBytes: 40, + count: 4, + totalCount: 4, + children: undefined, + id: 9, + parent: 7, + reportLeafIndex: 8, + }, + { + name: "js::Shape", + bytes: 30, + totalBytes: 30, + count: 3, + totalCount: 3, + children: undefined, + id: 8, + parent: 7, + reportLeafIndex: 7, + }, + ], + id: 7, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "objects", + count: 0, + totalCount: 3, + bytes: 0, + totalBytes: 30, + children: [ + { + name: "Array", + bytes: 20, + totalBytes: 20, + count: 2, + totalCount: 2, + children: undefined, + id: 4, + parent: 2, + reportLeafIndex: 3, + }, + { + name: "Function", + bytes: 10, + totalBytes: 10, + count: 1, + totalCount: 1, + children: undefined, + id: 3, + parent: 2, + reportLeafIndex: 2, + }, + ], + id: 2, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "strings", + count: 1, + totalCount: 1, + bytes: 10, + totalBytes: 10, + children: undefined, + id: 6, + parent: 1, + reportLeafIndex: 5, + }, + { + name: "scripts", + count: 1, + totalCount: 1, + bytes: 1, + totalBytes: 1, + children: undefined, + id: 5, + parent: 1, + reportLeafIndex: 4, + }, + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, +}; + +function run_test() { + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-03.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-03.js new file mode 100644 index 000000000..bdf932099 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-03.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests CensusTreeNode with `objectClass` breakdown. + */ + +const countBreakdown = { by: "count", count: true, bytes: true }; + +const BREAKDOWN = { + by: "objectClass", + then: countBreakdown, + other: { by: "internalType", then: countBreakdown } +}; + +const REPORT = { + "Function": { bytes: 10, count: 10 }, + "Array": { bytes: 100, count: 1 }, + "other": { + "JIT::CODE::NOW!!!": { bytes: 20, count: 2 }, + "JIT::CODE::LATER!!!": { bytes: 40, count: 4 } + } +}; + +const EXPECTED = { + name: null, + count: 0, + totalCount: 17, + bytes: 0, + totalBytes: 170, + children: [ + { + name: "Array", + bytes: 100, + totalBytes: 100, + count: 1, + totalCount: 1, + children: undefined, + id: 3, + parent: 1, + reportLeafIndex: 2, + }, + { + name: "other", + count: 0, + totalCount: 6, + bytes: 0, + totalBytes: 60, + children: [ + { + name: "JIT::CODE::LATER!!!", + bytes: 40, + totalBytes: 40, + count: 4, + totalCount: 4, + children: undefined, + id: 6, + parent: 4, + reportLeafIndex: 5, + }, + { + name: "JIT::CODE::NOW!!!", + bytes: 20, + totalBytes: 20, + count: 2, + totalCount: 2, + children: undefined, + id: 5, + parent: 4, + reportLeafIndex: 4, + }, + ], + id: 4, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "Function", + bytes: 10, + totalBytes: 10, + count: 10, + totalCount: 10, + children: undefined, + id: 2, + parent: 1, + reportLeafIndex: 1, + }, + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, +}; + +function run_test() { + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-04.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-04.js new file mode 100644 index 000000000..cc0c3bac0 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-04.js @@ -0,0 +1,159 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests CensusTreeNode with `allocationStack` breakdown. + */ + +function run_test() { + const countBreakdown = { by: "count", count: true, bytes: true }; + + const BREAKDOWN = { + by: "allocationStack", + then: countBreakdown, + noStack: countBreakdown, + }; + + let stack1, stack2, stack3, stack4, stack5; + + (function a() { + (function b() { + (function c() { + stack1 = saveStack(3); + }()); + (function d() { + stack2 = saveStack(3); + stack3 = saveStack(3); + }()); + stack4 = saveStack(2); + }()); + }()); + + stack5 = saveStack(1); + + const REPORT = new Map([ + [stack1, { bytes: 10, count: 1 }], + [stack2, { bytes: 20, count: 2 }], + [stack3, { bytes: 30, count: 3 }], + [stack4, { bytes: 40, count: 4 }], + [stack5, { bytes: 50, count: 5 }], + ["noStack", { bytes: 60, count: 6 }], + ]); + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 210, + count: 0, + totalCount: 21, + children: [ + { + name: stack4.parent, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: stack3.parent, + bytes: 0, + totalBytes: 50, + count: 0, + totalCount: 5, + children: [ + { + name: stack3, + bytes: 30, + totalBytes: 30, + count: 3, + totalCount: 3, + children: undefined, + id: 7, + parent: 5, + reportLeafIndex: 3, + }, + { + name: stack2, + bytes: 20, + totalBytes: 20, + count: 2, + totalCount: 2, + children: undefined, + id: 6, + parent: 5, + reportLeafIndex: 2, + } + ], + id: 5, + parent: 2, + reportLeafIndex: undefined, + }, + { + name: stack4, + bytes: 40, + totalBytes: 40, + count: 4, + totalCount: 4, + children: undefined, + id: 8, + parent: 2, + reportLeafIndex: 4, + }, + { + name: stack1.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: stack1, + bytes: 10, + totalBytes: 10, + count: 1, + totalCount: 1, + children: undefined, + id: 4, + parent: 3, + reportLeafIndex: 1, + }, + ], + id: 3, + parent: 2, + reportLeafIndex: undefined, + }, + ], + id: 2, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "noStack", + bytes: 60, + totalBytes: 60, + count: 6, + totalCount: 6, + children: undefined, + id: 10, + parent: 1, + reportLeafIndex: 6, + }, + { + name: stack5, + bytes: 50, + totalBytes: 50, + count: 5, + totalCount: 5, + children: undefined, + id: 9, + parent: 1, + reportLeafIndex: 5 + }, + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-05.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-05.js new file mode 100644 index 000000000..20fb76bd2 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-05.js @@ -0,0 +1,145 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests CensusTreeNode with `allocationStack` => `objectClass` breakdown. + */ + +function run_test() { + const countBreakdown = { by: "count", count: true, bytes: true }; + + const BREAKDOWN = { + by: "allocationStack", + then: { + by: "objectClass", + then: countBreakdown, + other: countBreakdown + }, + noStack: countBreakdown, + }; + + let stack; + + (function a() { + (function b() { + (function c() { + stack = saveStack(3); + }()); + }()); + }()); + + const REPORT = new Map([ + [stack, { Foo: { bytes: 10, count: 1 }, + Bar: { bytes: 20, count: 2 }, + Baz: { bytes: 30, count: 3 }, + other: { bytes: 40, count: 4 } + }], + ["noStack", { bytes: 50, count: 5 }], + ]); + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 150, + count: 0, + totalCount: 15, + children: [ + { + name: stack.parent.parent, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: stack.parent, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: stack, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: "other", + bytes: 40, + totalBytes: 40, + count: 4, + totalCount: 4, + children: undefined, + id: 8, + parent: 4, + reportLeafIndex: 5, + }, + { + name: "Baz", + bytes: 30, + totalBytes: 30, + count: 3, + totalCount: 3, + children: undefined, + id: 7, + parent: 4, + reportLeafIndex: 4, + }, + { + name: "Bar", + bytes: 20, + totalBytes: 20, + count: 2, + totalCount: 2, + children: undefined, + id: 6, + parent: 4, + reportLeafIndex: 3, + }, + { + name: "Foo", + bytes: 10, + totalBytes: 10, + count: 1, + totalCount: 1, + children: undefined, + id: 5, + parent: 4, + reportLeafIndex: 2, + }, + ], + id: 4, + parent: 3, + reportLeafIndex: undefined, + } + ], + id: 3, + parent: 2, + reportLeafIndex: undefined, + } + ], + id: 2, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "noStack", + bytes: 50, + totalBytes: 50, + count: 5, + totalCount: 5, + children: undefined, + id: 9, + parent: 1, + reportLeafIndex: 6, + }, + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-06.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-06.js new file mode 100644 index 000000000..eb1801207 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-06.js @@ -0,0 +1,200 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test inverting CensusTreeNode with a by alloaction stack breakdown. + */ + +function run_test() { + const BREAKDOWN = { + by: "allocationStack", + then: { by: "count", count: true, bytes: true }, + noStack: { by: "count", count: true, bytes: true }, + }; + + let stack1, stack2, stack3, stack4; + + function a(n) { + return b(n); + } + function b(n) { + return c(n); + } + function c(n) { + return saveStack(n); + } + function d(n) { + return b(n); + } + function e(n) { + return c(n); + } + + const abc_Stack = a(3); + const bc_Stack = b(2); + const c_Stack = c(1); + const dbc_Stack = d(3); + const ec_Stack = e(2); + + const REPORT = new Map([ + [abc_Stack, { bytes: 10, count: 1 }], + [ bc_Stack, { bytes: 10, count: 1 }], + [ c_Stack, { bytes: 10, count: 1 }], + [dbc_Stack, { bytes: 10, count: 1 }], + [ ec_Stack, { bytes: 10, count: 1 }], + ["noStack", { bytes: 50, count: 5 }], + ]); + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: "noStack", + bytes: 50, + totalBytes: 50, + count: 5, + totalCount: 5, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 16, + parent: 15, + reportLeafIndex: undefined, + } + ], + id: 15, + parent: 14, + reportLeafIndex: 6, + }, + { + name: abc_Stack, + bytes: 50, + totalBytes: 10, + count: 5, + totalCount: 1, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 18, + parent: 17, + reportLeafIndex: undefined, + }, + { + name: abc_Stack.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 22, + parent: 19, + reportLeafIndex: undefined, + }, + { + name: abc_Stack.parent.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 21, + parent: 20, + reportLeafIndex: undefined, + } + ], + id: 20, + parent: 19, + reportLeafIndex: undefined, + }, + { + name: dbc_Stack.parent.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 24, + parent: 23, + reportLeafIndex: undefined, + } + ], + id: 23, + parent: 19, + reportLeafIndex: undefined, + } + ], + id: 19, + parent: 17, + reportLeafIndex: undefined, + }, + { + name: ec_Stack.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: null, + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: undefined, + id: 26, + parent: 25, + reportLeafIndex: undefined, + }, + ], + id: 25, + parent: 17, + reportLeafIndex: undefined, + }, + ], + id: 17, + parent: 14, + reportLeafIndex: new Set([1, 2, 3, 4, 5]), + } + ], + id: 14, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { invert: true }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-07.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-07.js new file mode 100644 index 000000000..6bc085257 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-07.js @@ -0,0 +1,200 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test inverting CensusTreeNode with a non-allocation stack breakdown. + */ + +function run_test() { + const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other:{ + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + }; + + const REPORT = { + objects: { + Array: { bytes: 50, count: 5 }, + other: { bytes: 0, count: 0 }, + }, + scripts: { + "js::jit::JitScript": { bytes: 30, count: 3 }, + }, + strings: { + JSAtom: { bytes: 60, count: 6 }, + }, + other: { + "js::Shape": { bytes: 80, count: 8 }, + } + }; + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 220, + count: 0, + totalCount: 22, + children: [ + { + name: "js::Shape", + bytes: 80, + totalBytes: 80, + count: 8, + totalCount: 8, + children: [ + { + name: "other", + bytes: 0, + totalBytes: 80, + count: 0, + totalCount: 8, + children: [ + { + name: null, + bytes: 0, + totalBytes: 220, + count: 0, + totalCount: 22, + children: undefined, + id: 14, + parent: 13, + reportLeafIndex: undefined, + } + ], + id: 13, + parent: 12, + reportLeafIndex: undefined, + } + ], + id: 12, + parent: 11, + reportLeafIndex: 9, + }, + { + name: "JSAtom", + bytes: 60, + totalBytes: 60, + count: 6, + totalCount: 6, + children: [ + { + name: "strings", + bytes: 0, + totalBytes: 60, + count: 0, + totalCount: 6, + children: [ + { + name: null, + bytes: 0, + totalBytes: 220, + count: 0, + totalCount: 22, + children: undefined, + id: 17, + parent: 16, + reportLeafIndex: undefined, + } + ], + id: 16, + parent: 15, + reportLeafIndex: undefined, + } + ], + id: 15, + parent: 11, + reportLeafIndex: 7, + }, + { + name: "Array", + bytes: 50, + totalBytes: 50, + count: 5, + totalCount: 5, + children: [ + { + name: "objects", + bytes: 0, + totalBytes: 50, + count: 0, + totalCount: 5, + children: [ + { + name: null, + bytes: 0, + totalBytes: 220, + count: 0, + totalCount: 22, + children: undefined, + id: 20, + parent: 19, + reportLeafIndex: undefined, + } + ], + id: 19, + parent: 18, + reportLeafIndex: undefined, + } + ], + id: 18, + parent: 11, + reportLeafIndex: 2, + }, + { + name: "js::jit::JitScript", + bytes: 30, + totalBytes: 30, + count: 3, + totalCount: 3, + children: [ + { + name: "scripts", + bytes: 0, + totalBytes: 30, + count: 0, + totalCount: 3, + children: [ + { + name: null, + bytes: 0, + totalBytes: 220, + count: 0, + totalCount: 22, + children: undefined, + id: 23, + parent: 22, + reportLeafIndex: undefined, + } + ], + id: 22, + parent: 21, + reportLeafIndex: undefined, + } + ], + id: 21, + parent: 11, + reportLeafIndex: 5, + }, + ], + id: 11, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { invert: true }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-08.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-08.js new file mode 100644 index 000000000..1c686c810 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-08.js @@ -0,0 +1,142 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test inverting CensusTreeNode with a non-allocation stack breakdown. + */ + +function run_test() { + const BREAKDOWN = { + by: "filename", + then: { + by: "internalType", + then: { by: "count", count: true, bytes: true } + }, + noFilename: { + by: "internalType", + then: { by: "count", count: true, bytes: true } + }, + }; + + const REPORT = { + "http://example.com/app.js": { + JSScript: { count: 10, bytes: 100 } + }, + "http://example.com/ads.js": { + "js::LazyScript": { count: 20, bytes: 200 } + }, + "http://example.com/trackers.js": { + JSScript: { count: 30, bytes: 300 } + }, + noFilename: { + "js::jit::JitCode": { count: 40, bytes: 400 } + } + }; + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 1000, + count: 0, + totalCount: 100, + children: [ + { + name: "noFilename", + bytes: 0, + totalBytes: 400, + count: 0, + totalCount: 40, + children: [ + { + name: "js::jit::JitCode", + bytes: 400, + totalBytes: 400, + count: 40, + totalCount: 40, + children: undefined, + id: 9, + parent: 8, + reportLeafIndex: 8, + } + ], + id: 8, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "http://example.com/trackers.js", + bytes: 0, + totalBytes: 300, + count: 0, + totalCount: 30, + children: [ + { + name: "JSScript", + bytes: 300, + totalBytes: 300, + count: 30, + totalCount: 30, + children: undefined, + id: 7, + parent: 6, + reportLeafIndex: 6, + } + ], + id: 6, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "http://example.com/ads.js", + bytes: 0, + totalBytes: 200, + count: 0, + totalCount: 20, + children: [ + { + name: "js::LazyScript", + bytes: 200, + totalBytes: 200, + count: 20, + totalCount: 20, + children: undefined, + id: 5, + parent: 4, + reportLeafIndex: 4, + } + ], + id: 4, + parent: 1, + reportLeafIndex: undefined, + }, + { + name: "http://example.com/app.js", + bytes: 0, + totalBytes: 100, + count: 0, + totalCount: 10, + children: [ + { + name: "JSScript", + bytes: 100, + totalBytes: 100, + count: 10, + totalCount: 10, + children: undefined, + id: 3, + parent: 2, + reportLeafIndex: 2, + } + ], + id: 2, + parent: 1, + reportLeafIndex: undefined, + } + ], + id: 1, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-09.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-09.js new file mode 100644 index 000000000..3efed04b0 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-09.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test that repeatedly converting the same census report to a CensusTreeNode + * tree results in the same CensusTreeNode tree. + */ + +function run_test() { + const BREAKDOWN = { + by: "filename", + then: { + by: "internalType", + then: { by: "count", count: true, bytes: true } + }, + noFilename: { + by: "internalType", + then: { by: "count", count: true, bytes: true } + }, + }; + + const REPORT = { + "http://example.com/app.js": { + JSScript: { count: 10, bytes: 100 } + }, + "http://example.com/ads.js": { + "js::LazyScript": { count: 20, bytes: 200 } + }, + "http://example.com/trackers.js": { + JSScript: { count: 30, bytes: 300 } + }, + noFilename: { + "js::jit::JitCode": { count: 40, bytes: 400 } + } + }; + + const first = censusReportToCensusTreeNode(BREAKDOWN, REPORT); + const second = censusReportToCensusTreeNode(BREAKDOWN, REPORT); + const third = censusReportToCensusTreeNode(BREAKDOWN, REPORT); + + assertStructurallyEquivalent(first, second); + assertStructurallyEquivalent(second, third); +} diff --git a/dom/heapsnapshot/tests/unit/test_census-tree-node-10.js b/dom/heapsnapshot/tests/unit/test_census-tree-node-10.js new file mode 100644 index 000000000..b7798f23f --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census-tree-node-10.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Test when multiple leaves in the census report map to the same node in an + * inverted CensusReportTree. + */ + +function run_test() { + const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + }; + + const REPORT = { + objects: { + Array: { count: 1, bytes: 10 }, + }, + other: { + Array: { count: 1, bytes: 10 }, + }, + strings: { count: 0, bytes: 0 }, + scripts: { count: 0, bytes: 0 }, + }; + + const node = censusReportToCensusTreeNode(BREAKDOWN, REPORT, { invert: true }); + + equal(node.children[0].name, "Array"); + equal(node.children[0].reportLeafIndex.size, 2); + dumpn(`node.children[0].reportLeafIndex = ${[...node.children[0].reportLeafIndex]}`); + ok(node.children[0].reportLeafIndex.has(2)); + ok(node.children[0].reportLeafIndex.has(6)); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_01.js b/dom/heapsnapshot/tests/unit/test_census_diff_01.js new file mode 100644 index 000000000..75977bccb --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_01.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of breakdown by "internalType". + +const BREAKDOWN = { + by: "internalType", + then: { by: "count", count: true, bytes: true } +}; + +const REPORT1 = { + "JSObject": { + "count": 10, + "bytes": 100, + }, + "js::Shape": { + "count": 50, + "bytes": 500, + }, + "JSString": { + "count": 0, + "bytes": 0, + }, + "js::LazyScript": { + "count": 1, + "bytes": 10, + }, +}; + +const REPORT2 = { + "JSObject": { + "count": 11, + "bytes": 110, + }, + "js::Shape": { + "count": 51, + "bytes": 510, + }, + "JSString": { + "count": 1, + "bytes": 1, + }, + "js::BaseShape": { + "count": 1, + "bytes": 42, + }, +}; + +const EXPECTED = { + "JSObject": { + "count": 1, + "bytes": 10, + }, + "js::Shape": { + "count": 1, + "bytes": 10, + }, + "JSString": { + "count": 1, + "bytes": 1, + }, + "js::LazyScript": { + "count": -1, + "bytes": -10, + }, + "js::BaseShape": { + "count": 1, + "bytes": 42, + }, +}; + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_02.js b/dom/heapsnapshot/tests/unit/test_census_diff_02.js new file mode 100644 index 000000000..169e3f036 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_02.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of breakdown by "count". + +const BREAKDOWN = { by: "count", count: true, bytes: true }; + +const REPORT1 = { + "count": 10, + "bytes": 100, +}; + +const REPORT2 = { + "count": 11, + "bytes": 110, +}; + +const EXPECTED = { + "count": 1, + "bytes": 10, +}; + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_03.js b/dom/heapsnapshot/tests/unit/test_census_diff_03.js new file mode 100644 index 000000000..6dbca3e40 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_03.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of breakdown by "coarseType". + +const BREAKDOWN = { + by: "coarseType", + objects: { by: "count", count: true, bytes: true }, + scripts: { by: "count", count: true, bytes: true }, + strings: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +const REPORT1 = { + objects: { + count: 1, + bytes: 10, + }, + scripts: { + count: 1, + bytes: 10, + }, + strings: { + count: 1, + bytes: 10, + }, + other: { + count: 3, + bytes: 30, + }, +}; + +const REPORT2 = { + objects: { + count: 1, + bytes: 10, + }, + scripts: { + count: 0, + bytes: 0, + }, + strings: { + count: 2, + bytes: 20, + }, + other: { + count: 4, + bytes: 40, + }, +}; + +const EXPECTED = { + objects: { + count: 0, + bytes: 0, + }, + scripts: { + count: -1, + bytes: -10, + }, + strings: { + count: 1, + bytes: 10, + }, + other: { + count: 1, + bytes: 10, + }, +}; + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_04.js b/dom/heapsnapshot/tests/unit/test_census_diff_04.js new file mode 100644 index 000000000..a10097945 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_04.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of breakdown by "objectClass". + +const BREAKDOWN = { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, +}; + +const REPORT1 = { + "Array": { + count: 1, + bytes: 100, + }, + "Function": { + count: 10, + bytes: 10, + }, + "other": { + count: 10, + bytes: 100, + } +}; + +const REPORT2 = { + "Object": { + count: 1, + bytes: 100, + }, + "Function": { + count: 20, + bytes: 20, + }, + "other": { + count: 10, + bytes: 100, + } +}; + +const EXPECTED = { + "Array": { + count: -1, + bytes: -100, + }, + "Function": { + count: 10, + bytes: 10, + }, + "other": { + count: 0, + bytes: 0, + }, + "Object": { + count: 1, + bytes: 100, + }, +}; + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_05.js b/dom/heapsnapshot/tests/unit/test_census_diff_05.js new file mode 100644 index 000000000..b6d99f823 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_05.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of breakdown by "allocationStack". + +const BREAKDOWN = { + by: "allocationStack", + then: { by: "count", count: true, bytes: true }, + noStack: { by: "count", count: true, bytes: true }, +}; + +const stack1 = saveStack(); +const stack2 = saveStack(); +const stack3 = saveStack(); + +const REPORT1 = new Map([ + [stack1, { "count": 10, "bytes": 100 }], + [stack2, { "count": 1, "bytes": 10 }], +]); + +const REPORT2 = new Map([ + [stack2, { "count": 10, "bytes": 100 }], + [stack3, { "count": 1, "bytes": 10 }], +]); + +const EXPECTED = new Map([ + [stack1, { "count": -10, "bytes": -100 }], + [stack2, { "count": 9, "bytes": 90 }], + [stack3, { "count": 1, "bytes": 10 }], +]); + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_diff_06.js b/dom/heapsnapshot/tests/unit/test_census_diff_06.js new file mode 100644 index 000000000..430ff8c9c --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_diff_06.js @@ -0,0 +1,137 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test diffing census reports of a "complex" and "realistic" breakdown. + +const BREAKDOWN = { + by: "coarseType", + objects: { + by: "allocationStack", + then: { + by: "objectClass", + then: { by: "count", count: false, bytes: true }, + other: { by: "count", count: false, bytes: true } + }, + noStack: { + by: "objectClass", + then: { by: "count", count: false, bytes: true }, + other: { by: "count", count: false, bytes: true } + } + }, + strings: { + by: "internalType", + then: { by: "count", count: false, bytes: true } + }, + scripts: { + by: "internalType", + then: { by: "count", count: false, bytes: true } + }, + other: { + by: "internalType", + then: { by: "count", count: false, bytes: true } + }, +}; + +const stack1 = saveStack(); +const stack2 = saveStack(); +const stack3 = saveStack(); + +const REPORT1 = { + objects: new Map([ + [stack1, { Function: { bytes: 1 }, + Object: { bytes: 2 }, + other: { bytes: 0 }, + }], + [stack2, { Array: { bytes: 3 }, + Date: { bytes: 4 }, + other: { bytes: 0 }, + }], + ["noStack", { Object: { bytes: 3 }}], + ]), + strings: { + JSAtom: { bytes: 10 }, + JSLinearString: { bytes: 5 }, + }, + scripts: { + JSScript: { bytes: 1 }, + "js::jit::JitCode": { bytes: 2 }, + }, + other: { + "mozilla::dom::Thing": { bytes: 1 }, + } +}; + +const REPORT2 = { + objects: new Map([ + [stack2, { Array: { bytes: 1 }, + Date: { bytes: 2 }, + other: { bytes: 3 }, + }], + [stack3, { Function: { bytes: 1 }, + Object: { bytes: 2 }, + other: { bytes: 0 }, + }], + ["noStack", { Object: { bytes: 3 }}], + ]), + strings: { + JSAtom: { bytes: 5 }, + JSLinearString: { bytes: 10 }, + }, + scripts: { + JSScript: { bytes: 2 }, + "js::LazyScript": { bytes: 42 }, + "js::jit::JitCode": { bytes: 1 }, + }, + other: { + "mozilla::dom::OtherThing": { bytes: 1 }, + } +}; + +const EXPECTED = { + "objects": new Map([ + [stack1, { Function: { bytes: -1 }, + Object: { bytes: -2 }, + other: { bytes: 0 }, + }], + [stack2, { Array: { bytes: -2 }, + Date: { bytes: -2 }, + other: { bytes: 3 }, + }], + [stack3, { Function: { bytes: 1 }, + Object: { bytes: 2 }, + other: { bytes: 0 }, + }], + ["noStack", { Object: { bytes: 0 }}], + ]), + "scripts": { + "JSScript": { + "bytes": 1 + }, + "js::jit::JitCode": { + "bytes": -1 + }, + "js::LazyScript": { + "bytes": 42 + } + }, + "strings": { + "JSAtom": { + "bytes": -5 + }, + "JSLinearString": { + "bytes": 5 + } + }, + "other": { + "mozilla::dom::Thing": { + "bytes": -1 + }, + "mozilla::dom::OtherThing": { + "bytes": 1 + } + } +}; + +function run_test() { + assertDiff(BREAKDOWN, REPORT1, REPORT2, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_filtering_01.js b/dom/heapsnapshot/tests/unit/test_census_filtering_01.js new file mode 100644 index 000000000..57724d7c1 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_filtering_01.js @@ -0,0 +1,105 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test filtering basic CensusTreeNode trees. + +function run_test() { + const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other:{ + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + }; + + const REPORT = { + objects: { + Array: { bytes: 50, count: 5 }, + UInt8Array: { bytes: 80, count: 8 }, + Int32Array: { bytes: 320, count: 32 }, + other: { bytes: 0, count: 0 }, + }, + scripts: { + "js::jit::JitScript": { bytes: 30, count: 3 }, + }, + strings: { + JSAtom: { bytes: 60, count: 6 }, + }, + other: { + "js::Shape": { bytes: 80, count: 8 }, + } + }; + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 620, + count: 0, + totalCount: 62, + children: [ + { + name: "objects", + bytes: 0, + totalBytes: 450, + count: 0, + totalCount: 45, + children: [ + { + name: "Int32Array", + bytes: 320, + totalBytes: 320, + count: 32, + totalCount: 32, + children: undefined, + id: 15, + parent: 14, + reportLeafIndex: 4, + }, + { + name: "UInt8Array", + bytes: 80, + totalBytes: 80, + count: 8, + totalCount: 8, + children: undefined, + id: 16, + parent: 14, + reportLeafIndex: 3, + }, + { + name: "Array", + bytes: 50, + totalBytes: 50, + count: 5, + totalCount: 5, + children: undefined, + id: 17, + parent: 14, + reportLeafIndex: 2, + } + ], + id: 14, + parent: 13, + reportLeafIndex: undefined, + } + ], + id: 13, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { filter: "Array" }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_filtering_02.js b/dom/heapsnapshot/tests/unit/test_census_filtering_02.js new file mode 100644 index 000000000..0a57ce66d --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_filtering_02.js @@ -0,0 +1,124 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test filtering CensusTreeNode trees with an `allocationStack` breakdown. + +function run_test() { + const countBreakdown = { by: "count", count: true, bytes: true }; + + const BREAKDOWN = { + by: "allocationStack", + then: countBreakdown, + noStack: countBreakdown, + }; + + let stack1, stack2, stack3, stack4, stack5; + + (function foo() { + (function bar() { + (function baz() { + stack1 = saveStack(3); + }()); + (function quux() { + stack2 = saveStack(3); + stack3 = saveStack(3); + }()); + }()); + stack4 = saveStack(2); + }()); + + stack5 = saveStack(1); + + const REPORT = new Map([ + [stack1, { bytes: 10, count: 1 }], + [stack2, { bytes: 20, count: 2 }], + [stack3, { bytes: 30, count: 3 }], + [stack4, { bytes: 40, count: 4 }], + [stack5, { bytes: 50, count: 5 }], + ["noStack", { bytes: 60, count: 6 }], + ]); + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 210, + count: 0, + totalCount: 21, + children: [ + { + name: stack1.parent.parent, + bytes: 0, + totalBytes: 60, + count: 0, + totalCount: 6, + children: [ + { + name: stack2.parent, + bytes: 0, + totalBytes: 50, + count: 0, + totalCount: 5, + children: [ + { + name: stack3, + bytes: 30, + totalBytes: 30, + count: 3, + totalCount: 3, + children: undefined, + id: 15, + parent: 14, + reportLeafIndex: 3, + }, + { + name: stack2, + bytes: 20, + totalBytes: 20, + count: 2, + totalCount: 2, + children: undefined, + id: 16, + parent: 14, + reportLeafIndex: 2, + } + ], + id: 14, + parent: 13, + reportLeafIndex: undefined, + }, + { + name: stack1.parent, + bytes: 0, + totalBytes: 10, + count: 0, + totalCount: 1, + children: [ + { + name: stack1, + bytes: 10, + totalBytes: 10, + count: 1, + totalCount: 1, + children: undefined, + id: 18, + parent: 17, + reportLeafIndex: 1, + } + ], + id: 17, + parent: 13, + reportLeafIndex: undefined, + } + ], + id: 13, + parent: 12, + reportLeafIndex: undefined, + } + ], + id: 12, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { filter: "bar" }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_filtering_03.js b/dom/heapsnapshot/tests/unit/test_census_filtering_03.js new file mode 100644 index 000000000..2c69a14b8 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_filtering_03.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test filtering with no matches. + +function run_test() { + const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + scripts: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + strings: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + other:{ + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + }; + + const REPORT = { + objects: { + Array: { bytes: 50, count: 5 }, + UInt8Array: { bytes: 80, count: 8 }, + Int32Array: { bytes: 320, count: 32 }, + other: { bytes: 0, count: 0 }, + }, + scripts: { + "js::jit::JitScript": { bytes: 30, count: 3 }, + }, + strings: { + JSAtom: { bytes: 60, count: 6 }, + }, + other: { + "js::Shape": { bytes: 80, count: 8 }, + } + }; + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 620, + count: 0, + totalCount: 62, + children: undefined, + id: 13, + parent: undefined, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { filter: "zzzzzzzzzzzzzzzzzzzz" }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_filtering_04.js b/dom/heapsnapshot/tests/unit/test_census_filtering_04.js new file mode 100644 index 000000000..c9871436b --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_filtering_04.js @@ -0,0 +1,102 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the filtered nodes' counts and bytes are the same as they were when +// unfiltered. + +function run_test() { + const COUNT = { by: "count", count: true, bytes: true }; + const INTERNAL_TYPE = { by: "internalType", then: COUNT }; + + const BREAKDOWN = { + by: "coarseType", + objects: { by: "objectClass", then: COUNT, other: COUNT }, + strings: COUNT, + scripts: { + by: "filename", + then: INTERNAL_TYPE, + noFilename: INTERNAL_TYPE + }, + other: INTERNAL_TYPE, + }; + + const REPORT = { + objects: { + Function: { + count: 7, + bytes: 70 + }, + Array: { + count: 6, + bytes: 60 + } + }, + scripts: { + "http://mozilla.github.io/pdf.js/build/pdf.js": { + "js::LazyScript": { + count: 4, + bytes: 40 + }, + } + }, + strings: { + count: 2, + bytes: 20 + }, + other: { + "js::Shape": { + count: 1, + bytes: 10 + } + } + }; + + const EXPECTED = { + name: null, + bytes: 0, + totalBytes: 200, + count: 0, + totalCount: 20, + parent: undefined, + children: [ + { + name: "objects", + bytes: 0, + totalBytes: 130, + count: 0, + totalCount: 13, + children: [ + { + name: "Function", + bytes: 70, + totalBytes: 70, + count: 7, + totalCount: 7, + id: 13, + parent: 12, + children: undefined, + reportLeafIndex: 2, + }, + { + name: "Array", + bytes: 60, + totalBytes: 60, + count: 6, + totalCount: 6, + id: 14, + parent: 12, + children: undefined, + reportLeafIndex: 3, + }, + ], + id: 12, + parent: 11, + reportLeafIndex: undefined, + } + ], + id: 11, + reportLeafIndex: undefined, + }; + + compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, { filter: "objects" }); +} diff --git a/dom/heapsnapshot/tests/unit/test_census_filtering_05.js b/dom/heapsnapshot/tests/unit/test_census_filtering_05.js new file mode 100644 index 000000000..1d1f4fa55 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_census_filtering_05.js @@ -0,0 +1,71 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that filtered and inverted allocation stack census trees are sorted +// properly. + +function run_test() { + const countBreakdown = { by: "count", count: true, bytes: true }; + + const BREAKDOWN = { + by: "allocationStack", + then: countBreakdown, + noStack: countBreakdown, + }; + + const stacks = []; + + function foo(depth = 1) { + stacks.push(saveStack(depth)); + bar(depth + 1); + baz(depth + 1); + stacks.push(saveStack(depth)); + } + + function bar(depth = 1) { + stacks.push(saveStack(depth)); + stacks.push(saveStack(depth)); + } + + function baz(depth = 1) { + stacks.push(saveStack(depth)); + bang(depth + 1); + stacks.push(saveStack(depth)); + } + + function bang(depth = 1) { + stacks.push(saveStack(depth)); + stacks.push(saveStack(depth)); + stacks.push(saveStack(depth)); + } + + foo(); + bar(); + baz(); + bang(); + + const REPORT = new Map(stacks.map((s, i) => { + return [s, { + count: i + 1, + bytes: (i + 1) * 10 + }]; + })); + + const tree = censusReportToCensusTreeNode(BREAKDOWN, REPORT, { + filter: "baz", + invert: true + }); + + dumpn("tree = " + JSON.stringify(tree, savedFrameReplacer, 4)); + + (function assertSortedBySelf(node) { + if (node.children) { + let lastSelfBytes = Infinity; + for (let child of node.children) { + ok(child.bytes <= lastSelfBytes, `${child.bytes} <= ${lastSelfBytes}`); + lastSelfBytes = child.bytes; + assertSortedBySelf(child); + } + } + }(tree)); +} diff --git a/dom/heapsnapshot/tests/unit/test_countToBucketBreakdown_01.js b/dom/heapsnapshot/tests/unit/test_countToBucketBreakdown_01.js new file mode 100644 index 000000000..e89048c33 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_countToBucketBreakdown_01.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that we can turn a breakdown with { by: "count" } leaves into a +// breakdown with { by: "bucket" } leaves. + +const COUNT = { by: "count", count: true, bytes: true }; +const BUCKET = { by: "bucket" }; + +const BREAKDOWN = { + by: "coarseType", + objects: { by: "objectClass", then: COUNT, other: COUNT }, + strings: COUNT, + scripts: { + by: "filename", + then: { by: "internalType", then: COUNT }, + noFilename: { by: "internalType", then: COUNT }, + }, + other: { by: "internalType", then: COUNT }, +}; + +const EXPECTED = { + by: "coarseType", + objects: { by: "objectClass", then: BUCKET, other: BUCKET }, + strings: BUCKET, + scripts: { + by: "filename", + then: { by: "internalType", then: BUCKET }, + noFilename: { by: "internalType", then: BUCKET }, + }, + other: { by: "internalType", then: BUCKET }, +}; + +function run_test() { + assertCountToBucketBreakdown(BREAKDOWN, EXPECTED); +} diff --git a/dom/heapsnapshot/tests/unit/test_deduplicatePaths_01.js b/dom/heapsnapshot/tests/unit/test_deduplicatePaths_01.js new file mode 100644 index 000000000..418b49db3 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_deduplicatePaths_01.js @@ -0,0 +1,113 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test the behavior of the deduplicatePaths utility function. + +function edge(from, to, name) { + return { from, to, name }; +} + +function run_test() { + const a = 1; + const b = 2; + const c = 3; + const d = 4; + const e = 5; + const f = 6; + const g = 7; + + dumpn("Single long path"); + assertDeduplicatedPaths({ + target: g, + paths: [ + [ + pathEntry(a, "e1"), + pathEntry(b, "e2"), + pathEntry(c, "e3"), + pathEntry(d, "e4"), + pathEntry(e, "e5"), + pathEntry(f, "e6"), + ] + ], + expectedNodes: [a, b, c, d, e, f, g], + expectedEdges: [ + edge(a, b, "e1"), + edge(b, c, "e2"), + edge(c, d, "e3"), + edge(d, e, "e4"), + edge(e, f, "e5"), + edge(f, g, "e6"), + ] + }); + + dumpn("Multiple edges from and to the same nodes"); + assertDeduplicatedPaths({ + target: a, + paths: [ + [pathEntry(b, "x")], + [pathEntry(b, "y")], + [pathEntry(b, "z")], + ], + expectedNodes: [a, b], + expectedEdges: [ + edge(b, a, "x"), + edge(b, a, "y"), + edge(b, a, "z"), + ] + }); + + dumpn("Multiple paths sharing some nodes and edges"); + assertDeduplicatedPaths({ + target: g, + paths: [ + [ + pathEntry(a, "a->b"), + pathEntry(b, "b->c"), + pathEntry(c, "foo"), + ], + [ + pathEntry(a, "a->b"), + pathEntry(b, "b->d"), + pathEntry(d, "bar"), + ], + [ + pathEntry(a, "a->b"), + pathEntry(b, "b->e"), + pathEntry(e, "baz"), + ], + ], + expectedNodes: [a, b, c, d, e, g], + expectedEdges: [ + edge(a, b, "a->b"), + edge(b, c, "b->c"), + edge(b, d, "b->d"), + edge(b, e, "b->e"), + edge(c, g, "foo"), + edge(d, g, "bar"), + edge(e, g, "baz"), + ] + }); + + dumpn("Second shortest path contains target itself"); + assertDeduplicatedPaths({ + target: g, + paths: [ + [ + pathEntry(a, "a->b"), + pathEntry(b, "b->g"), + ], + [ + pathEntry(a, "a->b"), + pathEntry(b, "b->g"), + pathEntry(g, "g->f"), + pathEntry(f, "f->g"), + ], + ], + expectedNodes: [a, b, g], + expectedEdges: [ + edge(a, b, "a->b"), + edge(b, g, "b->g"), + ] + }); +} diff --git a/dom/heapsnapshot/tests/unit/test_getCensusIndividuals_01.js b/dom/heapsnapshot/tests/unit/test_getCensusIndividuals_01.js new file mode 100644 index 000000000..9c4f60991 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_getCensusIndividuals_01.js @@ -0,0 +1,60 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test basic functionality of `CensusUtils.getCensusIndividuals`. + +function run_test() { + const stack1 = saveStack(1); + const stack2 = saveStack(1); + const stack3 = saveStack(1); + + const COUNT = { by: "count", count: true, bytes: true }; + const INTERNAL_TYPE = { by: "internalType", then: COUNT }; + + const BREAKDOWN = { + by: "allocationStack", + then: INTERNAL_TYPE, + noStack: INTERNAL_TYPE, + }; + + const MOCK_SNAPSHOT = { + takeCensus: ({ breakdown }) => { + assertStructurallyEquivalent( + breakdown, + CensusUtils.countToBucketBreakdown(BREAKDOWN)); + + // DFS Index + return new Map([ // 0 + [stack1, { // 1 + JSObject: [101, 102, 103], // 2 + JSString: [111, 112, 113], // 3 + }], + [stack2, { // 4 + JSObject: [201, 202, 203], // 5 + JSString: [211, 212, 213], // 6 + }], + [stack3, { // 7 + JSObject: [301, 302, 303], // 8 + JSString: [311, 312, 313], // 9 + }], + ["noStack", { // 10 + JSObject: [401, 402, 403], // 11 + JSString: [411, 412, 413], // 12 + }], + ]); + } + }; + + const INDICES = new Set([3, 5, 9]); + + const EXPECTED = new Set([111, 112, 113, + 201, 202, 203, + 311, 312, 313]); + + const actual = new Set(CensusUtils.getCensusIndividuals(INDICES, + BREAKDOWN, + MOCK_SNAPSHOT)); + + assertStructurallyEquivalent(EXPECTED, actual); +} diff --git a/dom/heapsnapshot/tests/unit/test_getReportLeaves_01.js b/dom/heapsnapshot/tests/unit/test_getReportLeaves_01.js new file mode 100644 index 000000000..4c4298b6a --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_getReportLeaves_01.js @@ -0,0 +1,114 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test basic functionality of `CensusUtils.getReportLeaves`. + +function run_test() { + const BREAKDOWN = { + by: "coarseType", + objects: { + by: "objectClass", + then: { by: "count", count: true, bytes: true }, + other: { by: "count", count: true, bytes: true }, + }, + strings: { by: "count", count: true, bytes: true }, + scripts: { + by: "filename", + then: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + noFilename: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + }, + other: { + by: "internalType", + then: { by: "count", count: true, bytes: true }, + }, + }; + + const REPORT = { + objects: { + Array: { count: 6, bytes: 60 }, + Function: { count: 1, bytes: 10 }, + Object: { count: 1, bytes: 10 }, + RegExp: { count: 1, bytes: 10 }, + other: { count: 0, bytes: 0 }, + }, + strings: { count: 1, bytes: 10 }, + scripts: { + "foo.js": { + JSScript: { count: 1, bytes: 10 }, + "js::jit::IonScript": { count: 1, bytes: 10 }, + }, + noFilename: { + JSScript: { count: 1, bytes: 10 }, + "js::jit::IonScript": { count: 1, bytes: 10 }, + }, + }, + other: { + "js::Shape": { count: 7, bytes: 70 }, + "js::BaseShape": { count: 1, bytes: 10 }, + }, + }; + + const root = censusReportToCensusTreeNode(BREAKDOWN, REPORT); + dumpn("CensusTreeNode tree = " + JSON.stringify(root, null, 4)); + + (function assertEveryNodeCanFindItsLeaf(node) { + if (node.reportLeafIndex) { + const [ leaf ] = CensusUtils.getReportLeaves(new Set([node.reportLeafIndex]), + BREAKDOWN, + REPORT); + ok(leaf, "Should be able to find leaf for a node with a reportLeafIndex = " + node.reportLeafIndex); + } + + if (node.children) { + for (let child of node.children) { + assertEveryNodeCanFindItsLeaf(child); + } + } + }(root)); + + // Test finding multiple leaves at a time. + + function find(name, node) { + if (node.name === name) { + return node; + } + + if (node.children) { + for (let child of node.children) { + const found = find(name, child); + if (found) { + return found; + } + } + } + } + + const arrayNode = find("Array", root); + ok(arrayNode); + equal(typeof arrayNode.reportLeafIndex, "number"); + + const shapeNode = find("js::Shape", root); + ok(shapeNode); + equal(typeof shapeNode.reportLeafIndex, "number"); + + const indices = new Set([arrayNode.reportLeafIndex, shapeNode.reportLeafIndex]); + const leaves = CensusUtils.getReportLeaves(indices, BREAKDOWN, REPORT); + equal(leaves.length, 2); + + // `getReportLeaves` does not guarantee order of the results, so handle both + // cases. + ok(leaves.some(l => l === REPORT.objects.Array)); + ok(leaves.some(l => l === REPORT.other["js::Shape"])); + + // Test that bad indices do not yield results. + + const none = CensusUtils.getReportLeaves(new Set([999999999999]), BREAKDOWN, REPORT); + equal(none.length, 0); +} diff --git a/dom/heapsnapshot/tests/unit/test_saveHeapSnapshot_e10s_01.js b/dom/heapsnapshot/tests/unit/test_saveHeapSnapshot_e10s_01.js new file mode 100644 index 000000000..067b9effb --- /dev/null +++ b/dom/heapsnapshot/tests/unit/test_saveHeapSnapshot_e10s_01.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test saving a heap snapshot in the sandboxed e10s child process. + +function run_test() { + run_test_in_child("../unit/test_SaveHeapSnapshot.js"); +} diff --git a/dom/heapsnapshot/tests/unit/xpcshell.ini b/dom/heapsnapshot/tests/unit/xpcshell.ini new file mode 100644 index 000000000..f84b282d1 --- /dev/null +++ b/dom/heapsnapshot/tests/unit/xpcshell.ini @@ -0,0 +1,98 @@ +[DEFAULT] +tags = devtools heapsnapshot devtools-memory +head = head_heapsnapshot.js +tail = +firefox-appdir = browser +skip-if = toolkit == 'android' + +support-files = + Census.jsm + dominator-tree-worker.js + heap-snapshot-worker.js + Match.jsm + +[test_census_diff_01.js] +[test_census_diff_02.js] +[test_census_diff_03.js] +[test_census_diff_04.js] +[test_census_diff_05.js] +[test_census_diff_06.js] +[test_census_filtering_01.js] +[test_census_filtering_02.js] +[test_census_filtering_03.js] +[test_census_filtering_04.js] +[test_census_filtering_05.js] +[test_census-tree-node-01.js] +[test_census-tree-node-02.js] +[test_census-tree-node-03.js] +[test_census-tree-node-04.js] +[test_census-tree-node-05.js] +[test_census-tree-node-06.js] +[test_census-tree-node-07.js] +[test_census-tree-node-08.js] +[test_census-tree-node-09.js] +[test_census-tree-node-10.js] +[test_countToBucketBreakdown_01.js] +[test_deduplicatePaths_01.js] +[test_DominatorTree_01.js] +[test_DominatorTree_02.js] +[test_DominatorTree_03.js] +[test_DominatorTree_04.js] +[test_DominatorTree_05.js] +[test_DominatorTree_06.js] +[test_DominatorTreeNode_attachShortestPaths_01.js] +[test_DominatorTreeNode_getNodeByIdAlongPath_01.js] +[test_DominatorTreeNode_insert_01.js] +[test_DominatorTreeNode_insert_02.js] +[test_DominatorTreeNode_insert_03.js] +[test_DominatorTreeNode_LabelAndShallowSize_01.js] +[test_DominatorTreeNode_LabelAndShallowSize_02.js] +[test_DominatorTreeNode_LabelAndShallowSize_03.js] +[test_DominatorTreeNode_LabelAndShallowSize_04.js] +[test_DominatorTreeNode_partialTraversal_01.js] +[test_getCensusIndividuals_01.js] +[test_getReportLeaves_01.js] +[test_HeapAnalyses_computeDominatorTree_01.js] +[test_HeapAnalyses_computeDominatorTree_02.js] +[test_HeapAnalyses_deleteHeapSnapshot_01.js] +[test_HeapAnalyses_deleteHeapSnapshot_02.js] +[test_HeapAnalyses_deleteHeapSnapshot_03.js] +[test_HeapAnalyses_getCensusIndividuals_01.js] +[test_HeapAnalyses_getCreationTime_01.js] +[test_HeapAnalyses_getDominatorTree_01.js] +[test_HeapAnalyses_getDominatorTree_02.js] +[test_HeapAnalyses_getImmediatelyDominated_01.js] +[test_HeapAnalyses_readHeapSnapshot_01.js] +[test_HeapAnalyses_takeCensusDiff_01.js] +[test_HeapAnalyses_takeCensusDiff_02.js] +[test_HeapAnalyses_takeCensus_01.js] +[test_HeapAnalyses_takeCensus_02.js] +[test_HeapAnalyses_takeCensus_03.js] +[test_HeapAnalyses_takeCensus_04.js] +[test_HeapAnalyses_takeCensus_05.js] +[test_HeapAnalyses_takeCensus_06.js] +[test_HeapAnalyses_takeCensus_07.js] +[test_HeapSnapshot_creationTime_01.js] +[test_HeapSnapshot_deepStack_01.js] +[test_HeapSnapshot_describeNode_01.js] +[test_HeapSnapshot_computeShortestPaths_01.js] +[test_HeapSnapshot_computeShortestPaths_02.js] +[test_HeapSnapshot_takeCensus_01.js] +[test_HeapSnapshot_takeCensus_02.js] +[test_HeapSnapshot_takeCensus_03.js] +[test_HeapSnapshot_takeCensus_04.js] +[test_HeapSnapshot_takeCensus_05.js] +[test_HeapSnapshot_takeCensus_06.js] +[test_HeapSnapshot_takeCensus_07.js] +[test_HeapSnapshot_takeCensus_08.js] +[test_HeapSnapshot_takeCensus_09.js] +[test_HeapSnapshot_takeCensus_10.js] +[test_HeapSnapshot_takeCensus_11.js] +[test_HeapSnapshot_takeCensus_12.js] +[test_ReadHeapSnapshot.js] +[test_ReadHeapSnapshot_with_allocations.js] +skip-if = os == 'linux' # Bug 1176173 +[test_ReadHeapSnapshot_worker.js] +skip-if = os == 'linux' # Bug 1176173 +[test_SaveHeapSnapshot.js] +[test_saveHeapSnapshot_e10s_01.js] diff --git a/dom/html/HTMLLabelElement.h b/dom/html/HTMLLabelElement.h index c8385fc53..4057ffef6 100644 --- a/dom/html/HTMLLabelElement.h +++ b/dom/html/HTMLLabelElement.h @@ -59,8 +59,6 @@ public: using nsGenericHTMLElement::Focus; virtual void Focus(mozilla::ErrorResult& aError) override; - virtual bool IsDisabled() const override { return false; } - // nsIContent virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index f3ef20bfc..bc63eab51 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1871,6 +1871,20 @@ nsresult HTMLMediaElement::LoadResource() // Set the media element's CORS mode only when loading a resource mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); +#ifdef MOZ_EME + bool isBlob = false; + if (mMediaKeys && + Preferences::GetBool("media.eme.mse-only", true) && + // We only want mediaSource URLs, but they are BlobURL, so we have to + // check the schema and abort if they are not MediaStream or real Blob. + (NS_FAILED(mLoadingSrc->SchemeIs(BLOBURI_SCHEME, &isBlob)) || + !isBlob || + IsMediaStreamURI(mLoadingSrc) || + IsBlobURI(mLoadingSrc))) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } +#endif + HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc); if (other && other->mDecoder) { // Clone it. @@ -4000,7 +4014,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, return NS_ERROR_FAILURE; } } -#endif MediaEventSource<void>* waitingForKeyProducer = mDecoder->WaitingForKeyEvent(); // Not every decoder will produce waitingForKey events, only add ones that can @@ -4008,6 +4021,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder, mWaitingForKeyListener = waitingForKeyProducer->Connect( AbstractThread::MainThread(), this, &HTMLMediaElement::CannotDecryptWaitingForKey); } +#endif if (mChannelLoader) { mChannelLoader->Done(); @@ -6476,7 +6490,6 @@ HTMLMediaElement::GetTopLevelPrincipal() principal = doc->NodePrincipal(); return principal.forget(); } -#endif //MOZ_EME void HTMLMediaElement::CannotDecryptWaitingForKey() @@ -6495,6 +6508,7 @@ HTMLMediaElement::CannotDecryptWaitingForKey() UpdateReadyStateInternal(); } } +#endif //MOZ_EME NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture) { diff --git a/dom/html/HTMLObjectElement.h b/dom/html/HTMLObjectElement.h index 4041b78a3..5226154da 100644 --- a/dom/html/HTMLObjectElement.h +++ b/dom/html/HTMLObjectElement.h @@ -77,8 +77,6 @@ public: NS_IMETHOD Reset() override; NS_IMETHOD SubmitNamesValues(HTMLFormSubmission *aFormSubmission) override; - virtual bool IsDisabled() const override { return false; } - virtual void DoneAddingChildren(bool aHaveNotified) override; virtual bool IsDoneAddingChildren() override; diff --git a/dom/html/HTMLOptGroupElement.cpp b/dom/html/HTMLOptGroupElement.cpp index 8a044fbf3..9e738961d 100644 --- a/dom/html/HTMLOptGroupElement.cpp +++ b/dom/html/HTMLOptGroupElement.cpp @@ -53,15 +53,14 @@ HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mCanHandle = false; // Do not process any DOM events if the element is disabled // XXXsmaug This is not the right thing to do. But what is? - if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) { + if (IsDisabled()) { return NS_OK; } - nsIFrame* frame = GetPrimaryFrame(); - if (frame) { - const nsStyleUserInterface* uiStyle = frame->StyleUserInterface(); - if (uiStyle->mUserInput == StyleUserInput::None || - uiStyle->mUserInput == StyleUserInput::Disabled) { + if (nsIFrame* frame = GetPrimaryFrame()) { + // FIXME(emilio): This poking at the style of the frame is broken unless we + // flush before every event handling, which we don't really want to. + if (frame->StyleUserInterface()->mUserInput == StyleUserInput::None) { return NS_OK; } } diff --git a/dom/html/HTMLOptGroupElement.h b/dom/html/HTMLOptGroupElement.h index d53a2e32b..e46a6a953 100644 --- a/dom/html/HTMLOptGroupElement.h +++ b/dom/html/HTMLOptGroupElement.h @@ -46,10 +46,6 @@ public: virtual nsIDOMNode* AsDOMNode() override { return this; } - virtual bool IsDisabled() const override { - return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); - } - bool Disabled() const { return GetBoolAttr(nsGkAtoms::disabled); diff --git a/dom/html/HTMLOptionElement.h b/dom/html/HTMLOptionElement.h index e220b84df..4b5e192ff 100644 --- a/dom/html/HTMLOptionElement.h +++ b/dom/html/HTMLOptionElement.h @@ -67,10 +67,6 @@ public: nsresult CopyInnerTo(mozilla::dom::Element* aDest); - virtual bool IsDisabled() const override { - return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); - } - bool Disabled() const { return GetBoolAttr(nsGkAtoms::disabled); diff --git a/dom/html/HTMLOutputElement.h b/dom/html/HTMLOutputElement.h index 588262480..6b6c3f66c 100644 --- a/dom/html/HTMLOutputElement.h +++ b/dom/html/HTMLOutputElement.h @@ -35,8 +35,6 @@ public: NS_IMETHOD Reset() override; NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override; - virtual bool IsDisabled() const override { return false; } - nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, diff --git a/dom/html/HTMLTableCellElement.cpp b/dom/html/HTMLTableCellElement.cpp index d00d60400..1cf413413 100644 --- a/dom/html/HTMLTableCellElement.cpp +++ b/dom/html/HTMLTableCellElement.cpp @@ -390,26 +390,16 @@ HTMLTableCellElement::ParseAttribute(int32_t aNamespaceID, return aResult.ParseIntWithBounds(aValue, 0); } if (aAttribute == nsGkAtoms::colspan) { - bool res = aResult.ParseIntWithBounds(aValue, -1); - if (res) { - int32_t val = aResult.GetIntegerValue(); - // reset large colspan values as IE and opera do - if (val > MAX_COLSPAN || val <= 0) { - aResult.SetTo(1, &aValue); - } - } - return res; + aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN); + return true; } if (aAttribute == nsGkAtoms::rowspan) { - bool res = aResult.ParseIntWithBounds(aValue, -1, MAX_ROWSPAN); - if (res) { - int32_t val = aResult.GetIntegerValue(); - // quirks mode does not honor the special html 4 value of 0 - if (val < 0 || (0 == val && InNavQuirksMode(OwnerDoc()))) { + aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN); + // quirks mode does not honor the special html 4 value of 0 + if (aResult.GetIntegerValue() == 0 && InNavQuirksMode(OwnerDoc())) { aResult.SetTo(1, &aValue); - } } - return res; + return true; } if (aAttribute == nsGkAtoms::height) { return aResult.ParseSpecialIntValue(aValue); diff --git a/dom/html/HTMLTableCellElement.h b/dom/html/HTMLTableCellElement.h index 916333510..ab7a918eb 100644 --- a/dom/html/HTMLTableCellElement.h +++ b/dom/html/HTMLTableCellElement.h @@ -37,7 +37,7 @@ public: } void SetColSpan(uint32_t aColSpan, ErrorResult& aError) { - SetHTMLIntAttr(nsGkAtoms::colspan, aColSpan, aError); + SetUnsignedIntAttr(nsGkAtoms::colspan, aColSpan, 1, aError); } uint32_t RowSpan() const { @@ -45,7 +45,7 @@ public: } void SetRowSpan(uint32_t aRowSpan, ErrorResult& aError) { - SetHTMLIntAttr(nsGkAtoms::rowspan, aRowSpan, aError); + SetUnsignedIntAttr(nsGkAtoms::rowspan, aRowSpan, 1, aError); } //already_AddRefed<nsDOMTokenList> Headers() const; void GetHeaders(DOMString& aHeaders) diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 2f890325a..0c8bcc9c8 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2109,14 +2109,6 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) return nsGenericHTMLElement::PreHandleEvent(aVisitor); } -/* virtual */ -bool -nsGenericHTMLFormElement::IsDisabled() const -{ - return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) || - (mFieldSet && mFieldSet->IsDisabled()); -} - void nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset) { @@ -2308,14 +2300,13 @@ nsGenericHTMLFormElement::IsElementDisabledForEvents(EventMessage aMessage, break; } - bool disabled = IsDisabled(); - if (!disabled && aFrame) { - const nsStyleUserInterface* uiStyle = aFrame->StyleUserInterface(); - disabled = uiStyle->mUserInput == StyleUserInput::None || - uiStyle->mUserInput == StyleUserInput::Disabled; - + // FIXME(emilio): This poking at the style of the frame is slightly bogus + // unless we flush before every event, which we don't really want to do. + if (aFrame && + aFrame->StyleUserInterface()->mUserInput == StyleUserInput::None) { + return true; } - return disabled; + return IsDisabled(); } void diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 72039f266..acbebe087 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -817,8 +817,8 @@ public: /** * Returns the current disabled state of the element. */ - virtual bool IsDisabled() const { - return false; + bool IsDisabled() const { + return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); } bool IsHidden() const @@ -1222,8 +1222,6 @@ public: virtual nsresult PreHandleEvent( mozilla::EventChainPreVisitor& aVisitor) override; - virtual bool IsDisabled() const override; - /** * This callback is called by a fieldest on all its elements whenever its * disabled attribute is changed so the element knows its disabled state diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index 30dc9b6da..85f876cdc 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -3291,6 +3291,10 @@ BackgroundCursorChild::HandleResponse( auto& responses = const_cast<nsTArray<ObjectStoreCursorResponse>&>(aResponses); + // If a new cursor is created, we need to keep a reference to it until the + // ResultHelper creates a DOM Binding. + RefPtr<IDBCursor> newCursor; + for (ObjectStoreCursorResponse& response : responses) { StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); cloneReadInfo.mDatabase = mTransaction->Database(); @@ -3300,8 +3304,6 @@ BackgroundCursorChild::HandleResponse( nullptr, cloneReadInfo.mFiles); - RefPtr<IDBCursor> newCursor; - if (mCursor) { mCursor->Reset(Move(response.key()), Move(cloneReadInfo)); } else { diff --git a/dom/inputmethod/moz.build b/dom/inputmethod/moz.build index 84b3cb8ab..e6d994565 100644 --- a/dom/inputmethod/moz.build +++ b/dom/inputmethod/moz.build @@ -6,11 +6,8 @@ EXTRA_COMPONENTS += [ 'InputMethod.manifest', - 'MozKeyboard.js', -] - -EXTRA_PP_JS_MODULES += [ 'Keyboard.jsm', + 'MozKeyboard.js', ] JAR_MANIFESTS += ['jar.mn'] diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 70ec7e0ae..fcfe407e8 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1786,7 +1786,7 @@ interface nsIDOMWindowUtils : nsISupports { /** * In certain cases the event handling of nodes, form controls in practice, * may be disabled. Such cases are for example the existence of disabled - * attribute or -moz-user-input: none/disabled. + * attribute or -moz-user-input: none. */ boolean isNodeDisabledForEvents(in nsIDOMNode aNode); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index fdf0fcf3e..6b914372b 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -29,7 +29,6 @@ #include "mozilla/dom/DataTransfer.h" #include "mozilla/dom/DOMStorageIPC.h" #include "mozilla/dom/ExternalHelperAppChild.h" -#include "mozilla/dom/FlyWebPublishedServerIPC.h" #include "mozilla/dom/GetFilesHelper.h" #include "mozilla/dom/ProcessGlobal.h" #include "mozilla/dom/PushNotifier.h" @@ -150,8 +149,6 @@ #endif #include "mozilla/dom/File.h" -#include "mozilla/dom/PPresentationChild.h" -#include "mozilla/dom/PresentationIPCService.h" #include "mozilla/ipc/InputStreamUtils.h" #ifdef MOZ_WEBSPEECH @@ -1417,65 +1414,6 @@ ContentChild::SendPBlobConstructor(PBlobChild* aActor, return PContentChild::SendPBlobConstructor(aActor, aParams); } -PPresentationChild* -ContentChild::AllocPPresentationChild() -{ - MOZ_CRASH("We should never be manually allocating PPresentationChild actors"); - return nullptr; -} - -bool -ContentChild::DeallocPPresentationChild(PPresentationChild* aActor) -{ - delete aActor; - return true; -} - -PFlyWebPublishedServerChild* -ContentChild::AllocPFlyWebPublishedServerChild(const nsString& name, - const FlyWebPublishOptions& params) -{ - MOZ_CRASH("We should never be manually allocating PFlyWebPublishedServerChild actors"); - return nullptr; -} - -bool -ContentChild::DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) -{ - RefPtr<FlyWebPublishedServerChild> actor = - dont_AddRef(static_cast<FlyWebPublishedServerChild*>(aActor)); - return true; -} - -bool -ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe, - const nsString& aSessionId) -{ - nsCOMPtr<nsIDocShell> docShell = - do_GetInterface(static_cast<TabChild*>(aIframe)->WebNavigation()); - NS_WARNING_ASSERTION(docShell, "WebNavigation failed"); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - NS_WARNING_ASSERTION(service, "presentation service is missing"); - - Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())->MonitorResponderLoading(aSessionId, docShell))); - - return true; -} - -bool -ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) -{ - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - NS_WARNING_ASSERTION(service, "presentation service is missing"); - - Unused << NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER))); - - return true; -} - bool ContentChild::RecvNotifyEmptyHTTPCache() { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 4c8f15cc0..2b36560e2 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -297,23 +297,6 @@ public: virtual bool DeallocPStorageChild(PStorageChild* aActor) override; - virtual PPresentationChild* AllocPPresentationChild() override; - - virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override; - - virtual PFlyWebPublishedServerChild* - AllocPFlyWebPublishedServerChild(const nsString& name, - const FlyWebPublishOptions& params) override; - - virtual bool DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) override; - - virtual bool - RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe, - const nsString& aSessionId) override; - - virtual bool - RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override; - virtual bool RecvNotifyEmptyHTTPCache() override; virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 97e3a4880..0d11fb889 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -51,10 +51,7 @@ #include "mozilla/dom/ServiceWorkerRegistrar.h" #include "mozilla/dom/power/PowerManagerService.h" #include "mozilla/dom/Permissions.h" -#include "mozilla/dom/PresentationParent.h" -#include "mozilla/dom/PPresentationParent.h" #include "mozilla/dom/PushNotifier.h" -#include "mozilla/dom/FlyWebPublishedServerIPC.h" #include "mozilla/dom/quota/QuotaManagerService.h" #include "mozilla/dom/time/DateCacheCleaner.h" #include "mozilla/embedding/printingui/PrintingParent.h" @@ -3106,44 +3103,6 @@ ContentParent::DeallocPStorageParent(PStorageParent* aActor) return true; } -PPresentationParent* -ContentParent::AllocPPresentationParent() -{ - RefPtr<PresentationParent> actor = new PresentationParent(); - return actor.forget().take(); -} - -bool -ContentParent::DeallocPPresentationParent(PPresentationParent* aActor) -{ - RefPtr<PresentationParent> actor = - dont_AddRef(static_cast<PresentationParent*>(aActor)); - return true; -} - -bool -ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor) -{ - return static_cast<PresentationParent*>(aActor)->Init(mChildID); -} - -PFlyWebPublishedServerParent* -ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name, - const FlyWebPublishOptions& params) -{ - RefPtr<FlyWebPublishedServerParent> actor = - new FlyWebPublishedServerParent(name, params); - return actor.forget().take(); -} - -bool -ContentParent::DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) -{ - RefPtr<FlyWebPublishedServerParent> actor = - dont_AddRef(static_cast<FlyWebPublishedServerParent*>(aActor)); - return true; -} - PSpeechSynthesisParent* ContentParent::AllocPSpeechSynthesisParent() { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 26b5c44ac..f6f3e64db 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -800,18 +800,6 @@ private: virtual bool DeallocPStorageParent(PStorageParent* aActor) override; - virtual PPresentationParent* AllocPPresentationParent() override; - - virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override; - - virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override; - - virtual PFlyWebPublishedServerParent* - AllocPFlyWebPublishedServerParent(const nsString& name, - const FlyWebPublishOptions& params) override; - - virtual bool DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) override; - virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent() override; virtual bool diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e8fb25aec..5b15433d5 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -43,9 +43,7 @@ include protocol PJavaScript; include protocol PRemoteSpellcheckEngine; include protocol PWebBrowserPersistDocument; include protocol PWebrtcGlobal; -include protocol PPresentation; include protocol PVideoDecoderManager; -include protocol PFlyWebPublishedServer; include DOMTypes; include JavaScriptTypes; include InputStreamParams; @@ -88,7 +86,6 @@ using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/Structu using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h"; using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; -using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h"; union ChromeRegistryItem { @@ -260,8 +257,6 @@ nested(upto inside_cpow) sync protocol PContent manages PRemoteSpellcheckEngine; manages PWebBrowserPersistDocument; manages PWebrtcGlobal; - manages PPresentation; - manages PFlyWebPublishedServer; both: // Depending on exactly how the new browser is being created, it might be @@ -481,18 +476,6 @@ child: async UpdateWindow(uintptr_t aChildId); /** - * Notify the child that presentation receiver has been launched with the - * correspondent iframe. - */ - async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId); - - /** - * Notify the child that the info about a presentation receiver needs to be - * cleaned up. - */ - async NotifyPresentationReceiverCleanUp(nsString aSessionId); - - /** * Notify the child that cache is emptied. */ async NotifyEmptyHTTPCache(); @@ -682,10 +665,6 @@ parent: async PWebrtcGlobal(); - async PPresentation(); - - async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params); - // Services remoting async StartVisitedQuery(URIParams uri); diff --git a/dom/mathml/nsMathMLElement.cpp b/dom/mathml/nsMathMLElement.cpp index d6aef876c..2be931682 100644 --- a/dom/mathml/nsMathMLElement.cpp +++ b/dom/mathml/nsMathMLElement.cpp @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "mozilla/ArrayUtils.h" #include "nsGkAtoms.h" +#include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN #include "nsCRT.h" #include "nsLayoutStylesheetCache.h" #include "nsRuleData.h" @@ -150,8 +151,9 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID, const nsAString& aValue, nsAttrValue& aResult) { + MOZ_ASSERT(IsMathMLElement()); if (aNamespaceID == kNameSpaceID_None) { - if (IsMathMLElement(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) { + if (mNodeInfo->Equals(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) { WarnDeprecated(nsGkAtoms::mode->GetUTF16String(), nsGkAtoms::display->GetUTF16String(), OwnerDoc()); } @@ -165,6 +167,16 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID, aAttribute == nsGkAtoms::mathbackground_) { return aResult.ParseColor(aValue); } + if (mNodeInfo->Equals(nsGkAtoms::mtd_)) { + if (aAttribute == nsGkAtoms::columnspan_) { + aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN); + return true; + } + if (aAttribute == nsGkAtoms::rowspan) { + aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN); + return true; + } + } } return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute, @@ -209,6 +221,8 @@ static Element::MappedAttributeEntry sDirStyles[] = { bool nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const { + MOZ_ASSERT(IsMathMLElement()); + static const MappedAttributeEntry* const mtableMap[] = { sMtableStyles, sCommonPresStyles @@ -240,10 +254,10 @@ nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math)) return FindAttributeDependence(aAttribute, mstyleMap); - if (IsMathMLElement(nsGkAtoms::mtable_)) + if (mNodeInfo->Equals(nsGkAtoms::mtable_)) return FindAttributeDependence(aAttribute, mtableMap); - if (IsMathMLElement(nsGkAtoms::mrow_)) + if (mNodeInfo->Equals(nsGkAtoms::mrow_)) return FindAttributeDependence(aAttribute, mrowMap); if (IsAnyOfMathMLElements(nsGkAtoms::maction_, diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index a0f04ec98..6babcce17 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -31,7 +31,9 @@ class MediaResource; class ReentrantMonitor; class VideoFrameContainer; class MediaDecoderOwner; +#ifdef MOZ_EME class CDMProxy; +#endif typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags; diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 223c59c3b..0cce91ccb 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -387,7 +387,9 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner) , mLogicalPosition(0.0) , mDuration(std::numeric_limits<double>::quiet_NaN()) , mResourceCallback(new ResourceCallback()) +#ifdef MOZ_EME , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__)) +#endif , mIgnoreProgressData(false) , mInfiniteStream(false) , mOwner(aOwner) @@ -472,7 +474,9 @@ MediaDecoder::Shutdown() mResourceCallback->Disconnect(); +#ifdef MOZ_EME mCDMProxyPromiseHolder.RejectIfExists(true, __func__); +#endif DiscardOngoingSeekIfExists(); @@ -1537,6 +1541,7 @@ MediaDecoder::CanPlayThrough() return GetStatistics().CanPlayThrough(); } +#ifdef MOZ_EME RefPtr<MediaDecoder::CDMProxyPromise> MediaDecoder::RequestCDMProxy() const { @@ -1551,6 +1556,7 @@ MediaDecoder::SetCDMProxy(CDMProxy* aProxy) mCDMProxyPromiseHolder.ResolveIfExists(aProxy, __func__); } +#endif bool MediaDecoder::IsOpusEnabled() diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 05e88db8b..298552433 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -8,7 +8,11 @@ #define MediaDecoder_h_ #include "mozilla/Atomics.h" + +#ifdef MOZ_EME #include "mozilla/CDMProxy.h" +#endif + #include "mozilla/MozPromise.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/StateMirroring.h" @@ -432,6 +436,7 @@ private: MediaDecoderOwner* GetOwner() const override; +#ifdef MOZ_EME typedef MozPromise<RefPtr<CDMProxy>, bool /* aIgnored */, /* IsExclusive = */ true> CDMProxyPromise; // Resolved when a CDMProxy is available and the capabilities are known or @@ -439,6 +444,7 @@ private: RefPtr<CDMProxyPromise> RequestCDMProxy() const; void SetCDMProxy(CDMProxy* aProxy); +#endif static bool IsOggEnabled(); static bool IsOpusEnabled(); @@ -589,8 +595,10 @@ private: RefPtr<ResourceCallback> mResourceCallback; +#ifdef MOZ_EME MozPromiseHolder<CDMProxyPromise> mCDMProxyPromiseHolder; RefPtr<CDMProxyPromise> mCDMProxyPromise; +#endif protected: // The promise resolving/rejection is queued as a "micro-task" which will be diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index 8a6997826..f53c74689 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -24,7 +24,9 @@ namespace mozilla { +#ifdef MOZ_EME class CDMProxy; +#endif class MediaDecoderReader; struct WaitForDataRejectValue @@ -186,7 +188,9 @@ public: // when to call SetIdle(). virtual void SetIdle() {} +#ifdef MOZ_EME virtual void SetCDMProxy(CDMProxy* aProxy) {} +#endif // Tell the reader that the data decoded are not for direct playback, so it // can accept more files, in particular those which have more channels than diff --git a/dom/media/MediaDecoderReaderWrapper.h b/dom/media/MediaDecoderReaderWrapper.h index 92001ca33..1a8d3a68c 100644 --- a/dom/media/MediaDecoderReaderWrapper.h +++ b/dom/media/MediaDecoderReaderWrapper.h @@ -113,7 +113,9 @@ public: return mReader->CanonicalBuffered(); } +#ifdef MOZ_EME void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); } +#endif void SetVideoBlankDecode(bool aIsBlankDecode); diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 2ed1956c9..63222c22c 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -1238,7 +1238,12 @@ DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata) // feeding in the CDM, which we need to decode the first frame (and // thus get the metadata). We could fix this if we could compute the start // time by demuxing without necessaring decoding. - bool waitingForCDM = Info().IsEncrypted() && !mMaster->mCDMProxy; + bool waitingForCDM = +#ifdef MOZ_EME + mMaster->Info().IsEncrypted() && !mMaster->mCDMProxy; +#else + false; +#endif mMaster->mNotifyMetadataBeforeFirstFrame = mMaster->mDuration.Ref().isSome() || waitingForCDM; @@ -1262,7 +1267,9 @@ DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState) { if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) { // Exit dormant when the user wants to play. +#ifdef MOZ_EME MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy); +#endif MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent); SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Suppressed); } @@ -1575,7 +1582,9 @@ ShutdownState::Enter() // dispose of the timer. master->mVideoDecodeSuspendTimer.Reset(); +#ifdef MOZ_EME master->mCDMProxyPromise.DisconnectIfExists(); +#endif if (master->IsPlaying()) { master->StopPlayback(); @@ -2129,10 +2138,12 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder) mMediaSink = CreateMediaSink(mAudioCaptured); +#ifdef MOZ_EME mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then( OwnerThread(), __func__, this, &MediaDecoderStateMachine::OnCDMProxyReady, &MediaDecoderStateMachine::OnCDMProxyNotReady)); +#endif nsresult rv = mReader->Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -3108,6 +3119,7 @@ void MediaDecoderStateMachine::OnMediaSinkAudioError(nsresult aResult) DecodeError(MediaResult(NS_ERROR_DOM_MEDIA_MEDIASINK_ERR, __func__)); } +#ifdef MOZ_EME void MediaDecoderStateMachine::OnCDMProxyReady(RefPtr<CDMProxy> aProxy) { @@ -3124,6 +3136,7 @@ MediaDecoderStateMachine::OnCDMProxyNotReady() MOZ_ASSERT(OnTaskQueue()); mCDMProxyPromise.Complete(); } +#endif void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index ff3258ff1..f04f34983 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -768,10 +768,12 @@ private: // Playback will not start when audio is offloading. bool mAudioOffloading; +#ifdef MOZ_EME void OnCDMProxyReady(RefPtr<CDMProxy> aProxy); void OnCDMProxyNotReady(); RefPtr<CDMProxy> mCDMProxy; MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise; +#endif private: // The buffered range. Mirrored from the decoder thread. diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 773434710..396f31e37 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -4,7 +4,10 @@ * 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/. */ +#ifdef MOZ_EME #include "mozilla/CDMProxy.h" +#endif + #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/HTMLMediaElement.h" #include "mozilla/Preferences.h" @@ -611,7 +614,6 @@ private: nsTArray<uint8_t> mInitData; nsString mInitDataType; }; -#endif void MediaFormatReader::SetCDMProxy(CDMProxy* aProxy) @@ -624,6 +626,7 @@ MediaFormatReader::SetCDMProxy(CDMProxy* aProxy) }); OwnerThread()->Dispatch(r.forget()); } +#endif // MOZ_EME bool MediaFormatReader::IsWaitingOnCDMResource() { diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 4d05ca201..be0b7cd17 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -20,7 +20,9 @@ namespace mozilla { +#ifdef MOZ_EME class CDMProxy; +#endif class MediaFormatReader final : public MediaDecoderReader { @@ -91,7 +93,9 @@ public: return mTrackDemuxersMayBlock; } +#ifdef MOZ_EME void SetCDMProxy(CDMProxy* aProxy) override; +#endif // Returns a string describing the state of the decoder data. // Used for debugging purposes. @@ -584,7 +588,9 @@ private: RefPtr<VideoFrameContainer> mVideoFrameContainer; layers::ImageContainer* GetImageContainer(); +#ifdef MOZ_EME RefPtr<CDMProxy> mCDMProxy; +#endif RefPtr<GMPCrashHelper> mCrashHelper; diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index 0569b15ae..df36ae83c 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -13,8 +13,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp", "resource://gre/modules/media/PeerConnectionIdp.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport", "resource://gre/modules/media/RTCStatsReport.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", - "resource://gre/modules/AppConstants.jsm"); const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1"; const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1"; diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index b293c251b..bf937241c 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -10,7 +10,9 @@ #include "MP4Demuxer.h" #include "mozilla/Preferences.h" #include "nsCharSeparatedTokenizer.h" +#ifdef MOZ_EME #include "mozilla/CDMProxy.h" +#endif #include "mozilla/Logging.h" #include "mozilla/SharedThreadPool.h" #include "nsMimeTypes.h" diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp index 14b06cc72..fa6f2f4c8 100644 --- a/dom/media/gmp/GMPChild.cpp +++ b/dom/media/gmp/GMPChild.cpp @@ -22,7 +22,9 @@ #include "GMPUtils.h" #include "prio.h" #include "base/task.h" +#ifdef MOZ_EME #include "widevine-adapter/WidevineAdapter.h" +#endif using namespace mozilla::ipc; @@ -254,9 +256,13 @@ GMPChild::AnswerStartPlugin(const nsString& aAdapter) return false; } +#ifdef MOZ_EME bool isWidevine = aAdapter.EqualsLiteral("widevine"); GMPAdapter* adapter = (isWidevine) ? new WidevineAdapter() : nullptr; +#else + GMPAdapter* adapter = nullptr; +#endif if (!mGMPLoader->Load(libPath.get(), libPath.Length(), mNodeId.BeginWriting(), diff --git a/dom/media/gmp/GMPDecryptorParent.cpp b/dom/media/gmp/GMPDecryptorParent.cpp index 1f8b9a7a8..4f5402160 100644 --- a/dom/media/gmp/GMPDecryptorParent.cpp +++ b/dom/media/gmp/GMPDecryptorParent.cpp @@ -43,11 +43,13 @@ GMPDecryptorParent::~GMPDecryptorParent() bool GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId) { +#ifdef MOZ_EME if (!mIsOpen) { NS_WARNING("Trying to use a dead GMP decrypter!"); return false; } mCallback->SetDecryptorId(aId); +#endif return true; } @@ -202,6 +204,7 @@ bool GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId, const nsCString& aSessionId) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvSetSessionId(token=%u, sessionId='%s')", this, aCreateSessionId, aSessionId.get())); @@ -210,6 +213,7 @@ GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId, return false; } mCallback->SetSessionId(aCreateSessionId, aSessionId); +#endif return true; } @@ -217,6 +221,7 @@ bool GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId, const bool& aSuccess) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvResolveLoadSessionPromise(promiseId=%u)", this, aPromiseId)); @@ -225,12 +230,14 @@ GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId, return false; } mCallback->ResolveLoadSessionPromise(aPromiseId, aSuccess); +#endif return true; } bool GMPDecryptorParent::RecvResolvePromise(const uint32_t& aPromiseId) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvResolvePromise(promiseId=%u)", this, aPromiseId)); @@ -239,6 +246,7 @@ GMPDecryptorParent::RecvResolvePromise(const uint32_t& aPromiseId) return false; } mCallback->ResolvePromise(aPromiseId); +#endif return true; } @@ -266,6 +274,7 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId, const GMPDOMException& aException, const nsCString& aMessage) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')", this, aPromiseId, aException, aMessage.get())); @@ -274,10 +283,11 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId, return false; } mCallback->RejectPromise(aPromiseId, GMPExToNsresult(aException), aMessage); +#endif return true; } - +#ifdef MOZ_EME static dom::MediaKeyMessageType ToMediaKeyMessageType(GMPSessionMessageType aMessageType) { switch (aMessageType) { @@ -288,12 +298,14 @@ ToMediaKeyMessageType(GMPSessionMessageType aMessageType) { default: return dom::MediaKeyMessageType::License_request; }; }; +#endif bool GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId, const GMPSessionMessageType& aMessageType, nsTArray<uint8_t>&& aMessage) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvSessionMessage(sessionId='%s', type=%d, msg='%s')", this, aSessionId.get(), aMessageType, ToBase64(aMessage).get())); @@ -302,6 +314,7 @@ GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId, return false; } mCallback->SessionMessage(aSessionId, ToMediaKeyMessageType(aMessageType), aMessage); +#endif return true; } @@ -309,6 +322,7 @@ bool GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId, const double& aExpiryTime) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvExpirationChange(sessionId='%s', expiry=%lf)", this, aSessionId.get(), aExpiryTime)); @@ -317,12 +331,14 @@ GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId, return false; } mCallback->ExpirationChange(aSessionId, aExpiryTime); +#endif return true; } bool GMPDecryptorParent::RecvSessionClosed(const nsCString& aSessionId) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvSessionClosed(sessionId='%s')", this, aSessionId.get())); @@ -331,6 +347,7 @@ GMPDecryptorParent::RecvSessionClosed(const nsCString& aSessionId) return false; } mCallback->SessionClosed(aSessionId); +#endif return true; } @@ -340,6 +357,7 @@ GMPDecryptorParent::RecvSessionError(const nsCString& aSessionId, const uint32_t& aSystemCode, const nsCString& aMessage) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvSessionError(sessionId='%s', exception=%d, sysCode=%d, msg='%s')", this, aSessionId.get(), aException, aSystemCode, aMessage.get())); @@ -352,9 +370,11 @@ GMPDecryptorParent::RecvSessionError(const nsCString& aSessionId, GMPExToNsresult(aException), aSystemCode, aMessage); +#endif return true; } +#ifdef MOZ_EME static dom::MediaKeyStatus ToMediaKeyStatus(GMPMediaKeyStatus aStatus) { switch (aStatus) { @@ -368,11 +388,13 @@ ToMediaKeyStatus(GMPMediaKeyStatus aStatus) { default: return dom::MediaKeyStatus::Internal_error; } } +#endif bool GMPDecryptorParent::RecvBatchedKeyStatusChanged(const nsCString& aSessionId, InfallibleTArray<GMPKeyInformation>&& aKeyInfos) { +#ifdef MOZ_EME LOGD(("GMPDecryptorParent[%p]::RecvBatchedKeyStatusChanged(sessionId='%s', KeyInfos len='%d')", this, aSessionId.get(), aKeyInfos.Length())); @@ -392,9 +414,11 @@ GMPDecryptorParent::RecvBatchedKeyStatusChanged(const nsCString& aSessionId, } mCallback->BatchedKeyStatusChanged(aSessionId, cdmKeyInfos); } +#endif return true; } +#ifdef MOZ_EME DecryptStatus ToDecryptStatus(GMPErr aError) { @@ -405,12 +429,14 @@ ToDecryptStatus(GMPErr aError) default: return GenericErr; } } +#endif bool GMPDecryptorParent::RecvDecrypted(const uint32_t& aId, const GMPErr& aErr, InfallibleTArray<uint8_t>&& aBuffer) { +#ifdef MOZ_EME LOGV(("GMPDecryptorParent[%p]::RecvDecrypted(id=%d, err=%d)", this, aId, aErr)); @@ -419,6 +445,7 @@ GMPDecryptorParent::RecvDecrypted(const uint32_t& aId, return false; } mCallback->Decrypted(aId, ToDecryptStatus(aErr), aBuffer); +#endif return true; } diff --git a/dom/media/gmp/GMPDecryptorProxy.h b/dom/media/gmp/GMPDecryptorProxy.h index 0ef31fd92..ed16755f8 100644 --- a/dom/media/gmp/GMPDecryptorProxy.h +++ b/dom/media/gmp/GMPDecryptorProxy.h @@ -6,7 +6,9 @@ #ifndef GMPDecryptorProxy_h_ #define GMPDecryptorProxy_h_ +#ifdef MOZ_EME #include "mozilla/DecryptorProxyCallback.h" +#endif #include "GMPCallbackBase.h" #include "gmp-decryption.h" #include "nsString.h" @@ -15,8 +17,13 @@ namespace mozilla { class CryptoSample; } // namespace mozilla +#ifdef MOZ_EME class GMPDecryptorProxyCallback : public DecryptorProxyCallback, public GMPCallbackBase { +#else +class GMPDecryptorProxyCallback : public GMPCallbackBase { +#endif + public: virtual ~GMPDecryptorProxyCallback() {} }; diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index 234ed5c05..84603e973 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -30,8 +30,10 @@ using mozilla::ipc::GeckoChildProcessHost; #include "WMFDecoderModule.h" #endif +#ifdef MOZ_EME #include "mozilla/dom/WidevineCDMManifestBinding.h" #include "widevine-adapter/WidevineAdapter.h" +#endif namespace mozilla { @@ -654,6 +656,7 @@ GMPParent::ReadGMPMetaData() return ReadGMPInfoFile(infoFile); } +#ifdef MOZ_EME // Maybe this is the Widevine adapted plugin? nsCOMPtr<nsIFile> manifestFile; rv = mDirectory->Clone(getter_AddRefs(manifestFile)); @@ -662,6 +665,9 @@ GMPParent::ReadGMPMetaData() } manifestFile->AppendRelativePath(NS_LITERAL_STRING("manifest.json")); return ReadChromiumManifestFile(manifestFile); +#else + return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); +#endif } RefPtr<GenericPromise> @@ -754,6 +760,7 @@ GMPParent::ReadChromiumManifestFile(nsIFile* aFile) RefPtr<GenericPromise> GMPParent::ParseChromiumManifest(nsString aJSON) { +#ifdef MOZ_EME LOGD("%s: for '%s'", __FUNCTION__, NS_LossyConvertUTF16toASCII(aJSON).get()); MOZ_ASSERT(NS_IsMainThread()); @@ -791,6 +798,10 @@ GMPParent::ParseChromiumManifest(nsString aJSON) #endif return GenericPromise::CreateAndResolve(true, __func__); +#else // !MOZ_EME + MOZ_ASSERT_UNREACHABLE("don't call me if EME isn't enabled"); + return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); +#endif // !MOZ_EME } bool diff --git a/dom/media/gmp/moz.build b/dom/media/gmp/moz.build index 3b67fb5c9..79de6e1d9 100644 --- a/dom/media/gmp/moz.build +++ b/dom/media/gmp/moz.build @@ -35,8 +35,6 @@ EXPORTS += [ 'GMPAudioDecoderProxy.h', 'GMPAudioHost.h', 'GMPCallbackBase.h', - 'GMPCDMCallbackProxy.h', - 'GMPCDMProxy.h', 'GMPChild.h', 'GMPContentChild.h', 'GMPContentParent.h', @@ -73,6 +71,12 @@ EXPORTS += [ 'GMPVideoPlaneImpl.h', ] +if CONFIG['MOZ_EME']: + EXPORTS += [ + 'GMPCDMCallbackProxy.h', + 'GMPCDMProxy.h', + ] + # We link GMPLoader into xul on B2G/Fennec as its code does not need to be # covered by a DRM vendor's voucher. if CONFIG['OS_TARGET'] == 'Android': @@ -87,8 +91,6 @@ UNIFIED_SOURCES += [ 'GMPAudioDecoderChild.cpp', 'GMPAudioDecoderParent.cpp', 'GMPAudioHost.cpp', - 'GMPCDMCallbackProxy.cpp', - 'GMPCDMProxy.cpp', 'GMPChild.cpp', 'GMPContentChild.cpp', 'GMPContentParent.cpp', @@ -120,10 +122,16 @@ UNIFIED_SOURCES += [ 'GMPVideoPlaneImpl.cpp', ] -DIRS += [ - 'rlz', - 'widevine-adapter', -] +if CONFIG['MOZ_EME']: + UNIFIED_SOURCES += [ + 'GMPCDMCallbackProxy.cpp', + 'GMPCDMProxy.cpp', + ] + +DIRS += ['rlz'] + +if CONFIG['MOZ_EME']: + DIRS += ['widevine-adapter'] IPDL_SOURCES += [ 'GMPTypes.ipdlh', diff --git a/dom/media/gtest/MockMediaDecoderOwner.h b/dom/media/gtest/MockMediaDecoderOwner.h index 324f18141..1009ca30a 100644 --- a/dom/media/gtest/MockMediaDecoderOwner.h +++ b/dom/media/gtest/MockMediaDecoderOwner.h @@ -34,8 +34,10 @@ public: void DownloadProgressed() override {} void UpdateReadyState() override {} void FirstFrameLoaded() override {} +#ifdef MOZ_EME void DispatchEncrypted(const nsTArray<uint8_t>& aInitData, const nsAString& aInitDataType) override {} +#endif bool IsActive() const override { return true; } bool IsHidden() const override { return false; } void DownloadSuspended() override {} diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build index a7ea73807..ae059962c 100644 --- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -28,6 +28,11 @@ UNIFIED_SOURCES += [ 'TestWebMBuffered.cpp', ] +if CONFIG['MOZ_EME']: + UNIFIED_SOURCES += [ + 'TestEME.cpp', + ] + if CONFIG['MOZ_WEBM_ENCODER']: UNIFIED_SOURCES += [ 'TestVideoTrackEncoder.cpp', diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 21fb158b5..9a2d00ad8 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -84,7 +84,7 @@ private: nsTArray<uint8_t> mInitData; nsString mInitDataType; }; -#endif +#endif // MOZ_EME TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder, const nsACString& aType) diff --git a/dom/media/moz.build b/dom/media/moz.build index df8cb619d..41267a6ef 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -20,7 +20,6 @@ with Files('GetUserMedia*'): BUG_COMPONENT = component_av DIRS += [ - 'eme', 'encoder', 'flac', 'gmp', @@ -49,6 +48,9 @@ if CONFIG['MOZ_FMP4']: if CONFIG['MOZ_WEBRTC']: DIRS += ['bridge'] +if CONFIG['MOZ_EME']: + DIRS += ['eme'] + TEST_DIRS += [ 'gtest', ] diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index 5bfdcffb7..6e7241c46 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -24,7 +24,6 @@ #endif #include "GMPDecoderModule.h" -#include "mozilla/CDMProxy.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/SharedThreadPool.h" #include "mozilla/StaticPtr.h" @@ -37,7 +36,11 @@ #include "H264Converter.h" #include "AgnosticDecoderModule.h" + +#ifdef MOZ_EME +#include "mozilla/CDMProxy.h" #include "EMEDecoderModule.h" +#endif #include "DecoderDoctorDiagnostics.h" @@ -450,11 +453,13 @@ PDMFactory::GetDecoder(const TrackInfo& aTrackInfo, return pdm.forget(); } +#ifdef MOZ_EME void PDMFactory::SetCDMProxy(CDMProxy* aProxy) { RefPtr<PDMFactory> m = new PDMFactory(); mEMEPDM = new EMEDecoderModule(aProxy, m); } +#endif } // namespace mozilla diff --git a/dom/media/platforms/PDMFactory.h b/dom/media/platforms/PDMFactory.h index 94be7e928..2b43fa1ab 100644 --- a/dom/media/platforms/PDMFactory.h +++ b/dom/media/platforms/PDMFactory.h @@ -11,7 +11,9 @@ #include "mozilla/Function.h" #include "mozilla/StaticMutex.h" +#ifdef MOZ_EME class CDMProxy; +#endif namespace mozilla { @@ -38,12 +40,14 @@ public: bool Supports(const TrackInfo& aTrackInfo, DecoderDoctorDiagnostics* aDiagnostics) const; +#ifdef MOZ_EME // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or // decrypt-and-decode EME encrypted content. If the CDM only decrypts and // does not decode, we create a PDM and use that to create MediaDataDecoders // that we use on on aTaskQueue to decode the decrypted stream. // This is called on the decode task queue. void SetCDMProxy(CDMProxy* aProxy); +#endif static constexpr int kYUV400 = 0; static constexpr int kYUV420 = 1; diff --git a/dom/media/platforms/moz.build b/dom/media/platforms/moz.build index f5fb72c5d..c46d52e3f 100644 --- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -30,11 +30,13 @@ UNIFIED_SOURCES += [ ] DIRS += [ - 'agnostic/eme', 'agnostic/gmp', 'omx' ] +if CONFIG['MOZ_EME']: + DIRS += ['agnostic/eme'] + if CONFIG['MOZ_WMF']: DIRS += [ 'wmf' ]; diff --git a/dom/moz.build b/dom/moz.build index 54dc0510e..89c539b4b 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -52,10 +52,10 @@ DIRS += [ 'fetch', 'filehandle', 'filesystem', - 'flyweb', 'gamepad', 'geolocation', 'grid', + 'heapsnapshot', 'html', 'json', 'jsurl', @@ -107,8 +107,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['MOZ_SECUREELEMENT']: DIRS += ['secureelement'] -DIRS += ['presentation'] - TEST_DIRS += [ 'tests', 'imptests', diff --git a/dom/performance/Performance.h b/dom/performance/Performance.h index 4debecc90..c40dd8aff 100644 --- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -96,8 +96,10 @@ public: IMPL_EVENT_HANDLER(resourcetimingbufferfull) +#ifdef MOZ_DEVTOOLS_SERVER virtual void GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj) = 0; +#endif virtual nsDOMNavigationTiming* GetDOMTiming() const = 0; diff --git a/dom/performance/PerformanceMainThread.cpp b/dom/performance/PerformanceMainThread.cpp index 5a84f5fb1..b95a91ea6 100644 --- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -11,6 +11,7 @@ namespace mozilla { namespace dom { + NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread, @@ -18,8 +19,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread, NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming, mNavigation, mDocEntry) +#ifdef MOZ_DEVTOOLS_SERVER tmp->mMozMemory = nullptr; mozilla::DropJSObjects(this); +#endif NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread, @@ -31,7 +34,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread, Performance) +#ifdef MOZ_DEVTOOLS_SERVER NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory) +#endif NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance) @@ -55,9 +60,12 @@ PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow, PerformanceMainThread::~PerformanceMainThread() { +#ifdef MOZ_DEVTOOLS_SERVER mozilla::DropJSObjects(this); +#endif } +#ifdef MOZ_DEVTOOLS_SERVER void PerformanceMainThread::GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj) @@ -71,6 +79,7 @@ PerformanceMainThread::GetMozMemory(JSContext *aCx, aObj.set(mMozMemory); } +#endif PerformanceTiming* PerformanceMainThread::Timing() diff --git a/dom/performance/PerformanceMainThread.h b/dom/performance/PerformanceMainThread.h index 9f0e185fc..a90a53520 100644 --- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -34,8 +34,10 @@ public: DOMHighResTimeStamp CreationTime() const override; +#ifdef MOZ_DEVTOOLS_SERVER virtual void GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj) override; +#endif virtual nsDOMNavigationTiming* GetDOMTiming() const override { @@ -79,7 +81,9 @@ protected: nsCOMPtr<nsITimedChannel> mChannel; RefPtr<PerformanceTiming> mTiming; RefPtr<PerformanceNavigation> mNavigation; +#ifdef MOZ_DEVTOOLS_SERVER JS::Heap<JSObject*> mMozMemory; +#endif }; } // namespace dom diff --git a/dom/performance/PerformanceWorker.h b/dom/performance/PerformanceWorker.h index 346bdd026..ffe2a1998 100644 --- a/dom/performance/PerformanceWorker.h +++ b/dom/performance/PerformanceWorker.h @@ -43,11 +43,13 @@ public: DOMHighResTimeStamp CreationTime() const override; +#ifdef MOZ_DEVTOOLS_SERVER virtual void GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj) override { MOZ_CRASH("This should not be called on workers."); } +#endif virtual nsDOMNavigationTiming* GetDOMTiming() const override { diff --git a/dom/plugins/base/PluginPRLibrary.cpp b/dom/plugins/base/PluginPRLibrary.cpp index ecc55d455..57c6c57ab 100644 --- a/dom/plugins/base/PluginPRLibrary.cpp +++ b/dom/plugins/base/PluginPRLibrary.cpp @@ -21,43 +21,10 @@ static int gNotOptimized; #define CALLING_CONVENTION_HACK #endif -#ifdef MOZ_WIDGET_ANDROID -#include "AndroidBridge.h" -#include "android_npapi.h" -#include <android/log.h> -#undef ALOG -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoJavaEnv", ## args) -#endif - using namespace mozilla::layers; namespace mozilla { -#ifdef MOZ_WIDGET_ANDROID -nsresult -PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs, - NPPluginFuncs* pFuncs, NPError* error) -{ - JNIEnv* env = jni::GetEnvForThread(); - - mozilla::AutoLocalJNIFrame jniFrame(env); - - if (mNP_Initialize) { - *error = mNP_Initialize(bFuncs, pFuncs, env); - } else { - NP_InitializeFunc pfNP_Initialize = (NP_InitializeFunc) - PR_FindFunctionSymbol(mLibrary, "NP_Initialize"); - if (!pfNP_Initialize) - return NS_ERROR_FAILURE; - *error = pfNP_Initialize(bFuncs, pFuncs, env); - } - - // Save pointers to functions that get called through PluginLibrary itself. - mNPP_New = pFuncs->newp; - mNPP_ClearSiteData = pFuncs->clearsitedata; - mNPP_GetSitesWithData = pFuncs->getsiteswithdata; - return NS_OK; -} -#elif defined(XP_UNIX) && !defined(XP_MACOSX) +#if defined(XP_UNIX) && !defined(XP_MACOSX) nsresult PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp deleted file mode 100644 index bc47e8999..000000000 --- a/dom/plugins/base/android/ANPAudio.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- - * 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 "base/basictypes.h" -#include "AndroidBridge.h" - -#include <android/log.h> -#include <stdlib.h> -#include <time.h> - -#include "assert.h" -#include "ANPBase.h" -#include "nsIThread.h" -#include "nsThreadUtils.h" -#include "mozilla/Mutex.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPluginsAudio" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_audio_##name - -/* android.media.AudioTrack */ -struct AudioTrack { - jclass at_class; - jmethodID constructor; - jmethodID flush; - jmethodID pause; - jmethodID play; - jmethodID setvol; - jmethodID stop; - jmethodID write; - jmethodID getpos; - jmethodID getstate; - jmethodID release; -}; - -enum AudioTrackMode { - MODE_STATIC = 0, - MODE_STREAM = 1 -}; - -/* android.media.AudioManager */ -enum AudioManagerStream { - STREAM_VOICE_CALL = 0, - STREAM_SYSTEM = 1, - STREAM_RING = 2, - STREAM_MUSIC = 3, - STREAM_ALARM = 4, - STREAM_NOTIFICATION = 5, - STREAM_DTMF = 8 -}; - -/* android.media.AudioFormat */ -enum AudioFormatChannel { - CHANNEL_OUT_MONO = 4, - CHANNEL_OUT_STEREO = 12 -}; - -enum AudioFormatEncoding { - ENCODING_PCM_16BIT = 2, - ENCODING_PCM_8BIT = 3 -}; - -enum AudioFormatState { - STATE_UNINITIALIZED = 0, - STATE_INITIALIZED = 1, - STATE_NO_STATIC_DATA = 2 -}; - -static struct AudioTrack at; - -static jclass -init_jni_bindings(JNIEnv *jenv) { - jclass jc = - (jclass)jenv->NewGlobalRef(jenv->FindClass("android/media/AudioTrack")); - - at.constructor = jenv->GetMethodID(jc, "<init>", "(IIIIII)V"); - at.flush = jenv->GetMethodID(jc, "flush", "()V"); - at.pause = jenv->GetMethodID(jc, "pause", "()V"); - at.play = jenv->GetMethodID(jc, "play", "()V"); - at.setvol = jenv->GetMethodID(jc, "setStereoVolume", "(FF)I"); - at.stop = jenv->GetMethodID(jc, "stop", "()V"); - at.write = jenv->GetMethodID(jc, "write", "([BII)I"); - at.getpos = jenv->GetMethodID(jc, "getPlaybackHeadPosition", "()I"); - at.getstate = jenv->GetMethodID(jc, "getState", "()I"); - at.release = jenv->GetMethodID(jc, "release", "()V"); - - return jc; -} - -struct ANPAudioTrack { - jobject output_unit; - jclass at_class; - - unsigned int rate; - unsigned int channels; - unsigned int bufferSize; - unsigned int isStopped; - unsigned int keepGoing; - - mozilla::Mutex lock; - - void* user; - ANPAudioCallbackProc proc; - ANPSampleFormat format; - - ANPAudioTrack() : lock("ANPAudioTrack") { } -}; - -class AudioRunnable : public mozilla::Runnable -{ -public: - NS_DECL_NSIRUNNABLE - - AudioRunnable(ANPAudioTrack* aAudioTrack) { - mTrack = aAudioTrack; - } - - ANPAudioTrack* mTrack; -}; - -NS_IMETHODIMP -AudioRunnable::Run() -{ - PR_SetCurrentThreadName("Android Audio"); - - JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); - - mozilla::AutoLocalJNIFrame autoFrame(jenv, 2); - - jbyteArray bytearray = jenv->NewByteArray(mTrack->bufferSize); - if (!bytearray) { - LOG("AudioRunnable:: Run. Could not create bytearray"); - return NS_ERROR_FAILURE; - } - - jbyte *byte = jenv->GetByteArrayElements(bytearray, nullptr); - if (!byte) { - LOG("AudioRunnable:: Run. Could not create bytearray"); - return NS_ERROR_FAILURE; - } - - ANPAudioBuffer buffer; - buffer.channelCount = mTrack->channels; - buffer.format = mTrack->format; - buffer.bufferData = (void*) byte; - - while (true) - { - // reset the buffer size - buffer.size = mTrack->bufferSize; - - { - mozilla::MutexAutoLock lock(mTrack->lock); - - if (!mTrack->keepGoing) - break; - - // Get data from the plugin - mTrack->proc(kMoreData_ANPAudioEvent, mTrack->user, &buffer); - } - - if (buffer.size == 0) { - LOG("%p - kMoreData_ANPAudioEvent", mTrack); - continue; - } - - size_t wroteSoFar = 0; - jint retval; - do { - retval = jenv->CallIntMethod(mTrack->output_unit, - at.write, - bytearray, - wroteSoFar, - buffer.size - wroteSoFar); - if (retval < 0) { - LOG("%p - Write failed %d", mTrack, retval); - break; - } - - wroteSoFar += retval; - - } while(wroteSoFar < buffer.size); - } - - jenv->CallVoidMethod(mTrack->output_unit, at.release); - - jenv->DeleteGlobalRef(mTrack->output_unit); - jenv->DeleteGlobalRef(mTrack->at_class); - - delete mTrack; - - jenv->ReleaseByteArrayElements(bytearray, byte, 0); - - return NS_OK; -} - -ANPAudioTrack* -anp_audio_newTrack(uint32_t sampleRate, // sampling rate in Hz - ANPSampleFormat format, - int channelCount, // MONO=1, STEREO=2 - ANPAudioCallbackProc proc, - void* user) -{ - ANPAudioTrack *s = new ANPAudioTrack(); - if (s == nullptr) { - return nullptr; - } - - JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); - - s->at_class = init_jni_bindings(jenv); - s->rate = sampleRate; - s->channels = channelCount; - s->bufferSize = s->rate * s->channels; - s->isStopped = true; - s->keepGoing = false; - s->user = user; - s->proc = proc; - s->format = format; - - int jformat; - switch (format) { - case kPCM16Bit_ANPSampleFormat: - jformat = ENCODING_PCM_16BIT; - break; - case kPCM8Bit_ANPSampleFormat: - jformat = ENCODING_PCM_8BIT; - break; - default: - LOG("Unknown audio format. defaulting to 16bit."); - jformat = ENCODING_PCM_16BIT; - break; - } - - int jChannels; - switch (channelCount) { - case 1: - jChannels = CHANNEL_OUT_MONO; - break; - case 2: - jChannels = CHANNEL_OUT_STEREO; - break; - default: - LOG("Unknown channel count. defaulting to mono."); - jChannels = CHANNEL_OUT_MONO; - break; - } - - mozilla::AutoLocalJNIFrame autoFrame(jenv); - - jobject obj = jenv->NewObject(s->at_class, - at.constructor, - STREAM_MUSIC, - s->rate, - jChannels, - jformat, - s->bufferSize, - MODE_STREAM); - - if (autoFrame.CheckForException() || obj == nullptr) { - jenv->DeleteGlobalRef(s->at_class); - delete s; - return nullptr; - } - - jint state = jenv->CallIntMethod(obj, at.getstate); - - if (autoFrame.CheckForException() || state == STATE_UNINITIALIZED) { - jenv->DeleteGlobalRef(s->at_class); - delete s; - return nullptr; - } - - s->output_unit = jenv->NewGlobalRef(obj); - return s; -} - -void -anp_audio_deleteTrack(ANPAudioTrack* s) -{ - if (s == nullptr) { - return; - } - - mozilla::MutexAutoLock lock(s->lock); - s->keepGoing = false; - - // deallocation happens in the AudioThread. There is a - // potential leak if anp_audio_start is never called, but - // we do not see that from flash. -} - -void -anp_audio_start(ANPAudioTrack* s) -{ - if (s == nullptr || s->output_unit == nullptr) { - return; - } - - if (s->keepGoing) { - // we are already playing. Ignore. - return; - } - - JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); - - mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); - jenv->CallVoidMethod(s->output_unit, at.play); - - if (autoFrame.CheckForException()) { - jenv->DeleteGlobalRef(s->at_class); - delete s; - return; - } - - s->isStopped = false; - s->keepGoing = true; - - // AudioRunnable now owns the ANPAudioTrack - RefPtr<AudioRunnable> runnable = new AudioRunnable(s); - - nsCOMPtr<nsIThread> thread; - NS_NewThread(getter_AddRefs(thread), runnable); -} - -void -anp_audio_pause(ANPAudioTrack* s) -{ - if (s == nullptr || s->output_unit == nullptr) { - return; - } - - JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); - - mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); - jenv->CallVoidMethod(s->output_unit, at.pause); -} - -void -anp_audio_stop(ANPAudioTrack* s) -{ - if (s == nullptr || s->output_unit == nullptr) { - return; - } - - s->isStopped = true; - JNIEnv* const jenv = mozilla::jni::GetEnvForThread(); - - mozilla::AutoLocalJNIFrame autoFrame(jenv, 0); - jenv->CallVoidMethod(s->output_unit, at.stop); -} - -bool -anp_audio_isStopped(ANPAudioTrack* s) -{ - return s->isStopped; -} - -uint32_t -anp_audio_trackLatency(ANPAudioTrack* s) { - // Hardcode an estimate of the system's audio latency. Flash hardcodes - // similar latency estimates for pre-Honeycomb devices that do not support - // ANPAudioTrackInterfaceV1's trackLatency(). The Android stock browser - // calls android::AudioTrack::latency(), an internal Android API that is - // not available in the public NDK: - // https://github.com/android/platform_external_webkit/commit/49bf866973cb3b2a6c74c0eab864e9562e4cbab1 - return 100; // milliseconds -} - -void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, newTrack); - ASSIGN(i, deleteTrack); - ASSIGN(i, start); - ASSIGN(i, pause); - ASSIGN(i, stop); - ASSIGN(i, isStopped); -} - -void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, newTrack); - ASSIGN(i, deleteTrack); - ASSIGN(i, start); - ASSIGN(i, pause); - ASSIGN(i, stop); - ASSIGN(i, isStopped); - ASSIGN(i, trackLatency); -} diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h deleted file mode 100644 index f9712dc79..000000000 --- a/dom/plugins/base/android/ANPBase.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: IDL; 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 <stdlib.h> -#include "android_npapi.h" -#include "nsISupportsImpl.h" - -#define NOT_IMPLEMENTED_FATAL() do { \ - __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins", \ - "%s not implemented %s, %d", \ - __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - abort(); \ - } while(0) - -#define NOT_IMPLEMENTED() \ - __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins", \ - "!!!!!!!!!!!!!! %s not implemented %s, %d", \ - __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - -void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i); -void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i); -void InitCanvasInterface(ANPCanvasInterfaceV0 *i); -void InitEventInterface(ANPEventInterfaceV0 *i); -void InitLogInterface(ANPLogInterfaceV0 *i); -void InitPaintInterface(ANPPaintInterfaceV0 *i); -void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i); -void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i); -void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i); -void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i); -void InitWindowInterface(ANPWindowInterfaceV0 *i); -void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i); -void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i); -void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i); -void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i); diff --git a/dom/plugins/base/android/ANPEvent.cpp b/dom/plugins/base/android/ANPEvent.cpp deleted file mode 100644 index cac8c94f0..000000000 --- a/dom/plugins/base/android/ANPEvent.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: IDL; 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 "assert.h" -#include "ANPBase.h" -#include <android/log.h> -#include "nsThreadUtils.h" -#include "nsNPAPIPluginInstance.h" -#include "nsNPAPIPlugin.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_event_##name - -void -anp_event_postEvent(NPP instance, const ANPEvent* event) -{ - LOG("%s", __PRETTY_FUNCTION__); - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - pinst->PostEvent((void*) event); - - LOG("returning from %s", __PRETTY_FUNCTION__); -} - - -void InitEventInterface(ANPEventInterfaceV0 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, postEvent); -} diff --git a/dom/plugins/base/android/ANPKeyCodes.h b/dom/plugins/base/android/ANPKeyCodes.h deleted file mode 100644 index edfe2b95c..000000000 --- a/dom/plugins/base/android/ANPKeyCodes.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2008, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ANPKeyCodes_DEFINED -#define ANPKeyCodes_DEFINED - -/* List the key codes that are set to a plugin in the ANPKeyEvent. - - These exactly match the values in android/view/KeyEvent.java and the - corresponding .h file android/keycodes.h. -*/ -enum ANPKeyCodes { - kUnknown_ANPKeyCode = 0, - - kSoftLeft_ANPKeyCode = 1, - kSoftRight_ANPKeyCode = 2, - kHome_ANPKeyCode = 3, - kBack_ANPKeyCode = 4, - kCall_ANPKeyCode = 5, - kEndCall_ANPKeyCode = 6, - k0_ANPKeyCode = 7, - k1_ANPKeyCode = 8, - k2_ANPKeyCode = 9, - k3_ANPKeyCode = 10, - k4_ANPKeyCode = 11, - k5_ANPKeyCode = 12, - k6_ANPKeyCode = 13, - k7_ANPKeyCode = 14, - k8_ANPKeyCode = 15, - k9_ANPKeyCode = 16, - kStar_ANPKeyCode = 17, - kPound_ANPKeyCode = 18, - kDpadUp_ANPKeyCode = 19, - kDpadDown_ANPKeyCode = 20, - kDpadLeft_ANPKeyCode = 21, - kDpadRight_ANPKeyCode = 22, - kDpadCenter_ANPKeyCode = 23, - kVolumeUp_ANPKeyCode = 24, - kVolumeDown_ANPKeyCode = 25, - kPower_ANPKeyCode = 26, - kCamera_ANPKeyCode = 27, - kClear_ANPKeyCode = 28, - kA_ANPKeyCode = 29, - kB_ANPKeyCode = 30, - kC_ANPKeyCode = 31, - kD_ANPKeyCode = 32, - kE_ANPKeyCode = 33, - kF_ANPKeyCode = 34, - kG_ANPKeyCode = 35, - kH_ANPKeyCode = 36, - kI_ANPKeyCode = 37, - kJ_ANPKeyCode = 38, - kK_ANPKeyCode = 39, - kL_ANPKeyCode = 40, - kM_ANPKeyCode = 41, - kN_ANPKeyCode = 42, - kO_ANPKeyCode = 43, - kP_ANPKeyCode = 44, - kQ_ANPKeyCode = 45, - kR_ANPKeyCode = 46, - kS_ANPKeyCode = 47, - kT_ANPKeyCode = 48, - kU_ANPKeyCode = 49, - kV_ANPKeyCode = 50, - kW_ANPKeyCode = 51, - kX_ANPKeyCode = 52, - kY_ANPKeyCode = 53, - kZ_ANPKeyCode = 54, - kComma_ANPKeyCode = 55, - kPeriod_ANPKeyCode = 56, - kAltLeft_ANPKeyCode = 57, - kAltRight_ANPKeyCode = 58, - kShiftLeft_ANPKeyCode = 59, - kShiftRight_ANPKeyCode = 60, - kTab_ANPKeyCode = 61, - kSpace_ANPKeyCode = 62, - kSym_ANPKeyCode = 63, - kExplorer_ANPKeyCode = 64, - kEnvelope_ANPKeyCode = 65, - kNewline_ANPKeyCode = 66, - kDel_ANPKeyCode = 67, - kGrave_ANPKeyCode = 68, - kMinus_ANPKeyCode = 69, - kEquals_ANPKeyCode = 70, - kLeftBracket_ANPKeyCode = 71, - kRightBracket_ANPKeyCode = 72, - kBackslash_ANPKeyCode = 73, - kSemicolon_ANPKeyCode = 74, - kApostrophe_ANPKeyCode = 75, - kSlash_ANPKeyCode = 76, - kAt_ANPKeyCode = 77, - kNum_ANPKeyCode = 78, - kHeadSetHook_ANPKeyCode = 79, - kFocus_ANPKeyCode = 80, - kPlus_ANPKeyCode = 81, - kMenu_ANPKeyCode = 82, - kNotification_ANPKeyCode = 83, - kSearch_ANPKeyCode = 84, - kMediaPlayPause_ANPKeyCode = 85, - kMediaStop_ANPKeyCode = 86, - kMediaNext_ANPKeyCode = 87, - kMediaPrevious_ANPKeyCode = 88, - kMediaRewind_ANPKeyCode = 89, - kMediaFastForward_ANPKeyCode = 90, - kMute_ANPKeyCode = 91, - kPageUp_ANPKeyCode = 92, - kPageDown_ANPKeyCode = 93, - kPictsymbols_ANPKeyCode = 94, - kSwitchCharset_ANPKeyCode = 95, - kButtonA_ANPKeyCode = 96, - kButtonB_ANPKeyCode = 97, - kButtonC_ANPKeyCode = 98, - kButtonX_ANPKeyCode = 99, - kButtonY_ANPKeyCode = 100, - kButtonZ_ANPKeyCode = 101, - kButtonL1_ANPKeyCode = 102, - kButtonR1_ANPKeyCode = 103, - kButtonL2_ANPKeyCode = 104, - kButtonR2_ANPKeyCode = 105, - kButtonThumbL_ANPKeyCode = 106, - kButtonThumbR_ANPKeyCode = 107, - kButtonStart_ANPKeyCode = 108, - kButtonSelect_ANPKeyCode = 109, - kButtonMode_ANPKeyCode = 110, - - // NOTE: If you add a new keycode here you must also add it to several other files. - // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. -}; - -#endif diff --git a/dom/plugins/base/android/ANPLog.cpp b/dom/plugins/base/android/ANPLog.cpp deleted file mode 100644 index 7ce13107b..000000000 --- a/dom/plugins/base/android/ANPLog.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: IDL; 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 "assert.h" -#include "ANPBase.h" -#include <android/log.h> - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_log_##name - -void -anp_log_log(ANPLogType type, const char format[], ...) { - - va_list argp; - va_start(argp,format); - __android_log_vprint(type == kError_ANPLogType ? ANDROID_LOG_ERROR : type == kWarning_ANPLogType ? - ANDROID_LOG_WARN : ANDROID_LOG_INFO, "GeckoPluginLog", format, argp); - va_end(argp); -} - -void InitLogInterface(ANPLogInterfaceV0 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, log); -} diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp deleted file mode 100644 index 88b43bd4a..000000000 --- a/dom/plugins/base/android/ANPNativeWindow.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: IDL; 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/. */ - -// must include config.h first for webkit to fiddle with new/delete -#include <android/log.h> -#include "ANPBase.h" -#include "nsIPluginInstanceOwner.h" -#include "nsPluginInstanceOwner.h" -#include "nsNPAPIPluginInstance.h" -#include "gfxRect.h" - -using namespace mozilla; - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name - -static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - return pinst->AcquireContentWindow(); -} - -static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { - // NativeWindow is TopLeft if uninverted. - gl::OriginPos newOriginPos = gl::OriginPos::TopLeft; - if (isContentInverted) - newOriginPos = gl::OriginPos::BottomLeft; - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - pinst->SetOriginPos(newOriginPos); - pinst->RedrawPlugin(); -} - - -void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) { - ASSIGN(i, acquireNativeWindow); - ASSIGN(i, invertPluginContent); -} diff --git a/dom/plugins/base/android/ANPSurface.cpp b/dom/plugins/base/android/ANPSurface.cpp deleted file mode 100644 index b6a699f28..000000000 --- a/dom/plugins/base/android/ANPSurface.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: IDL; 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 <dlfcn.h> -#include <android/log.h> -#include "ANPBase.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_surface_##name - -#define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear(); - -#define ANDROID_REGION_SIZE 512 - -enum { - PIXEL_FORMAT_RGBA_8888 = 1, - PIXEL_FORMAT_RGB_565 = 4, -}; - -struct SurfaceInfo { - uint32_t w; - uint32_t h; - uint32_t s; - uint32_t usage; - uint32_t format; - unsigned char* bits; - uint32_t reserved[2]; -}; - -typedef struct ARect { - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; -} ARect; - - -// used to cache JNI method and field IDs for Surface Objects -static struct ANPSurfaceInterfaceJavaGlue { - bool initialized; - jmethodID getSurfaceHolder; - jmethodID getSurface; - jfieldID surfacePointer; -} gSurfaceJavaGlue; - -static struct ANPSurfaceFunctions { - bool initialized; - - int (* lock)(void*, SurfaceInfo*, void*); - int (* unlockAndPost)(void*); - - void* (* regionConstructor)(void*); - void (* setRegion)(void*, ARect const&); -} gSurfaceFunctions; - - -static inline void* getSurface(JNIEnv* env, jobject view) { - if (!env || !view) { - return nullptr; - } - - if (!gSurfaceJavaGlue.initialized) { - - jclass surfaceViewClass = env->FindClass("android/view/SurfaceView"); - gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;"); - - jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder"); - gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;"); - - jclass surfaceClass = env->FindClass("android/view/Surface"); - gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, - "mSurfacePointer", "I"); - - if (!gSurfaceJavaGlue.surfacePointer) { - CLEAR_EXCEPTION(env); - - // It was something else in 2.2. - gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, - "mSurface", "I"); - - if (!gSurfaceJavaGlue.surfacePointer) { - CLEAR_EXCEPTION(env); - - // And something else in 2.3+ - gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, - "mNativeSurface", "I"); - - CLEAR_EXCEPTION(env); - } - } - - if (!gSurfaceJavaGlue.surfacePointer) { - LOG("Failed to acquire surface pointer"); - return nullptr; - } - - env->DeleteLocalRef(surfaceClass); - env->DeleteLocalRef(surfaceViewClass); - env->DeleteLocalRef(surfaceHolderClass); - - gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != nullptr); - } - - jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder); - jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface); - jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer); - - env->DeleteLocalRef(holder); - env->DeleteLocalRef(surface); - - return (void*)surfacePointer; -} - -static ANPBitmapFormat convertPixelFormat(int32_t format) { - switch (format) { - case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat; - case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat; - default: return kUnknown_ANPBitmapFormat; - } -} - -static int bytesPerPixel(int32_t format) { - switch (format) { - case PIXEL_FORMAT_RGBA_8888: return 4; - case PIXEL_FORMAT_RGB_565: return 2; - default: return -1; - } -} - -static bool init() { - if (gSurfaceFunctions.initialized) - return true; - - void* handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY); - - if (!handle) { - LOG("Failed to open libsurfaceflinger_client.so"); - return false; - } - - gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb"); - gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); - - - if (!gSurfaceFunctions.lock) { - // Stuff changed in 3.0/4.0 - handle = dlopen("libgui.so", RTLD_LAZY); - gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"); - gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); - } - - handle = dlopen("libui.so", RTLD_LAZY); - if (!handle) { - LOG("Failed to open libui.so"); - return false; - } - - gSurfaceFunctions.regionConstructor = (void* (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev"); - gSurfaceFunctions.setRegion = (void (*)(void*, ARect const&))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE"); - - gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost && - gSurfaceFunctions.regionConstructor && gSurfaceFunctions.setRegion); - LOG("Initialized? %d\n", gSurfaceFunctions.initialized); - return gSurfaceFunctions.initialized; -} - -// FIXME: All of this should be changed to use the equivalent things in AndroidBridge, bug 758612 -static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) { - if (!bitmap || !surfaceView) { - return false; - } - - void* surface = getSurface(env, surfaceView); - - if (!bitmap || !surface) { - return false; - } - - if (!init()) { - return false; - } - - void* region = nullptr; - if (dirtyRect) { - region = malloc(ANDROID_REGION_SIZE); - gSurfaceFunctions.regionConstructor(region); - - ARect rect; - rect.left = dirtyRect->left; - rect.top = dirtyRect->top; - rect.right = dirtyRect->right; - rect.bottom = dirtyRect->bottom; - - gSurfaceFunctions.setRegion(region, rect); - } - - SurfaceInfo info; - int err = gSurfaceFunctions.lock(surface, &info, region); - if (err < 0) { - LOG("Failed to lock surface"); - return false; - } - - // the surface may have expanded the dirty region so we must to pass that - // information back to the plugin. - if (dirtyRect) { - ARect* dirtyBounds = (ARect*)region; // The bounds are the first member, so this should work! - - dirtyRect->left = dirtyBounds->left; - dirtyRect->right = dirtyBounds->right; - dirtyRect->top = dirtyBounds->top; - dirtyRect->bottom = dirtyBounds->bottom; - } - - if (region) - free(region); - - int bpr = info.s * bytesPerPixel(info.format); - - bitmap->format = convertPixelFormat(info.format); - bitmap->width = info.w; - bitmap->height = info.h; - bitmap->rowBytes = bpr; - - if (info.w > 0 && info.h > 0) { - bitmap->baseAddr = info.bits; - } else { - bitmap->baseAddr = nullptr; - return false; - } - - return true; -} - -static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) { - if (!surfaceView) { - return; - } - - if (!init()) { - return; - } - - void* surface = getSurface(env, surfaceView); - - if (!surface) { - return; - } - - gSurfaceFunctions.unlockAndPost(surface); -} - -/////////////////////////////////////////////////////////////////////////////// - -void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) { - ASSIGN(i, lock); - ASSIGN(i, unlock); - - // setup the java glue struct - gSurfaceJavaGlue.initialized = false; - - // setup the function struct - gSurfaceFunctions.initialized = false; -} diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp deleted file mode 100644 index d5b2a7710..000000000 --- a/dom/plugins/base/android/ANPSystem.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: IDL; 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 "base/basictypes.h" - -#include "ANPBase.h" -#include "GeneratedJNIWrappers.h" -#include "PluginPRLibrary.h" -#include "assert.h" -#include "nsNPAPIPluginInstance.h" -#include "nsNPAPIPlugin.h" - -#include <android/log.h> - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_system_##name - -const char* -anp_system_getApplicationDataDirectory(NPP instance) -{ - static const char *dir = nullptr; - static const char *privateDir = nullptr; - - bool isPrivate = false; - - if (!dir) { - dir = getenv("ANDROID_PLUGIN_DATADIR"); - } - - if (!privateDir) { - privateDir = getenv("ANDROID_PLUGIN_DATADIR_PRIVATE"); - } - - if (!instance) { - return dir; - } - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - if (pinst && NS_SUCCEEDED(pinst->IsPrivateBrowsing(&isPrivate)) && isPrivate) { - return privateDir; - } - - return dir; -} - -const char* -anp_system_getApplicationDataDirectory() -{ - return anp_system_getApplicationDataDirectory(nullptr); -} - -jclass anp_system_loadJavaClass(NPP instance, const char* className) -{ - LOG("%s", __PRETTY_FUNCTION__); - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - mozilla::PluginPRLibrary* lib = static_cast<mozilla::PluginPRLibrary*>(pinst->GetPlugin()->GetLibrary()); - - nsCString libName; - lib->GetLibraryPath(libName); - - return mozilla::java::GeckoAppShell::LoadPluginClass(className, libName).Forget(); -} - -void anp_system_setPowerState(NPP instance, ANPPowerState powerState) -{ - nsNPAPIPluginInstance* pinst = nsNPAPIPluginInstance::GetFromNPP(instance); - - if (pinst) { - pinst->SetWakeLock(powerState == kScreenOn_ANPPowerState); - } -} - -void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, getApplicationDataDirectory); - ASSIGN(i, loadJavaClass); - ASSIGN(i, setPowerState); -} - -void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, getApplicationDataDirectory); - ASSIGN(i, loadJavaClass); - ASSIGN(i, setPowerState); -} diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp deleted file mode 100644 index 185ab1194..000000000 --- a/dom/plugins/base/android/ANPVideo.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* 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 <android/log.h> -#include "ANPBase.h" -#include "nsIPluginInstanceOwner.h" -#include "nsPluginInstanceOwner.h" -#include "nsNPAPIPluginInstance.h" -#include "gfxRect.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_video_##name - -using namespace mozilla; - -typedef nsNPAPIPluginInstance::VideoInfo VideoInfo; - -static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - - return pinst->AcquireVideoWindow(); -} - -static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window, - const ANPRectF* dimensions) { - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - - gfxRect rect(dimensions->left, dimensions->top, - dimensions->right - dimensions->left, - dimensions->bottom - dimensions->top); - - pinst->SetVideoDimensions(window, rect); - pinst->RedrawPlugin(); -} - -static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) { - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - pinst->ReleaseVideoWindow(window); - pinst->RedrawPlugin(); -} - -static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) { - // Bug 722682 - NOT_IMPLEMENTED(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) { - ASSIGN(i, acquireNativeWindow); - ASSIGN(i, setWindowDimensions); - ASSIGN(i, releaseNativeWindow); - ASSIGN(i, setFramerateCallback); -} diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp deleted file mode 100644 index e9003aff5..000000000 --- a/dom/plugins/base/android/ANPWindow.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -*- 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 "base/basictypes.h" -#include "assert.h" -#include "ANPBase.h" -#include <android/log.h> -#include "nsNPAPIPluginInstance.h" -#include "nsPluginInstanceOwner.h" -#include "nsWindow.h" -#include "mozilla/dom/ScreenOrientation.h" - -#undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_window_##name - -using namespace mozilla; -using namespace mozilla::widget; -using namespace mozilla::dom; - -void -anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) -{ - NOT_IMPLEMENTED(); -} - -void -anp_window_clearVisibleRects(NPP instance) -{ - NOT_IMPLEMENTED(); -} - -void -anp_window_showKeyboard(NPP instance, bool value) -{ - InputContext context; - context.mIMEState.mEnabled = value ? IMEState::PLUGIN : IMEState::DISABLED; - context.mIMEState.mOpen = value ? IMEState::OPEN : IMEState::CLOSED; - context.mActionHint.Assign(EmptyString()); - - InputContextAction action; - action.mCause = InputContextAction::CAUSE_UNKNOWN; - action.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED; - - nsWindow* window = nsWindow::TopWindow(); - if (!window) { - LOG("Couldn't get top window?"); - return; - } - - window->SetInputContext(context, action); -} - -void -anp_window_requestFullScreen(NPP instance) -{ - nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - - RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner(); - if (!owner) { - return; - } - - owner->RequestFullScreen(); -} - -void -anp_window_exitFullScreen(NPP instance) -{ - nsNPAPIPluginInstance* inst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - - RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner(); - if (!owner) { - return; - } - - owner->ExitFullScreen(); -} - -void -anp_window_requestCenterFitZoom(NPP instance) -{ - NOT_IMPLEMENTED(); -} - -ANPRectI -anp_window_visibleRect(NPP instance) -{ - ANPRectI rect = { 0, 0, 0, 0 }; - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - - nsIntSize currentSize = pinst->CurrentSize(); - rect.left = rect.top = 0; - rect.right = currentSize.width; - rect.bottom = currentSize.height; - - return rect; -} - -void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation) -{ - short newOrientation; - - // Convert to the ActivityInfo equivalent - switch (orientation) { - case kFixedLandscape_ANPScreenOrientation: - newOrientation = eScreenOrientation_LandscapePrimary; - break; - case kFixedPortrait_ANPScreenOrientation: - newOrientation = eScreenOrientation_PortraitPrimary; - break; - case kLandscape_ANPScreenOrientation: - newOrientation = eScreenOrientation_LandscapePrimary | - eScreenOrientation_LandscapeSecondary; - break; - case kPortrait_ANPScreenOrientation: - newOrientation = eScreenOrientation_PortraitPrimary | - eScreenOrientation_PortraitSecondary; - break; - default: - newOrientation = eScreenOrientation_None; - break; - } - - nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata); - pinst->SetFullScreenOrientation(newOrientation); -} - -void InitWindowInterface(ANPWindowInterfaceV0 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, setVisibleRects); - ASSIGN(i, clearVisibleRects); - ASSIGN(i, showKeyboard); - ASSIGN(i, requestFullScreen); - ASSIGN(i, exitFullScreen); - ASSIGN(i, requestCenterFitZoom); -} - -void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, setVisibleRects); - ASSIGN(i, clearVisibleRects); - ASSIGN(i, showKeyboard); - ASSIGN(i, requestFullScreen); - ASSIGN(i, exitFullScreen); - ASSIGN(i, requestCenterFitZoom); - ASSIGN(i, visibleRect); - ASSIGN(i, requestFullScreenOrientation); -} diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h deleted file mode 100644 index 16463d799..000000000 --- a/dom/plugins/base/android/android_npapi.h +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * Copyright 2009, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* Defines the android-specific types and functions as part of npapi - - In particular, defines the window and event types that are passed to - NPN_GetValue, NPP_SetWindow and NPP_HandleEvent. - - To minimize what native libraries the plugin links against, some - functionality is provided via function-ptrs (e.g. time, sound) - */ - -#ifndef android_npapi_H -#define android_npapi_H - -#include <stdint.h> -#include <jni.h> -#include "npapi.h" -#include "GLDefs.h" - -/////////////////////////////////////////////////////////////////////////////// -// General types - -enum ANPBitmapFormats { - kUnknown_ANPBitmapFormat = 0, - kRGBA_8888_ANPBitmapFormat = 1, - kRGB_565_ANPBitmapFormat = 2 -}; -typedef int32_t ANPBitmapFormat; - -struct ANPPixelPacking { - uint8_t AShift; - uint8_t ABits; - uint8_t RShift; - uint8_t RBits; - uint8_t GShift; - uint8_t GBits; - uint8_t BShift; - uint8_t BBits; -}; - -struct ANPBitmap { - void* baseAddr; - ANPBitmapFormat format; - int32_t width; - int32_t height; - int32_t rowBytes; -}; - -struct ANPRectF { - float left; - float top; - float right; - float bottom; -}; - -struct ANPRectI { - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; -}; - -struct ANPCanvas; -struct ANPMatrix; -struct ANPPaint; -struct ANPPath; -struct ANPTypeface; - -/////////////////////////////////////////////////////////////////////////////// -// NPN_GetValue - -/** queries for a specific ANPInterface. - - Maybe called with NULL for the NPP instance - - NPN_GetValue(inst, interface_enum, ANPInterface*) - */ -#define kLogInterfaceV0_ANPGetValue ((NPNVariable)1000) -#define kAudioTrackInterfaceV0_ANPGetValue ((NPNVariable)1001) -#define kCanvasInterfaceV0_ANPGetValue ((NPNVariable)1002) -#define kMatrixInterfaceV0_ANPGetValue ((NPNVariable)1003) -#define kPaintInterfaceV0_ANPGetValue ((NPNVariable)1004) -#define kPathInterfaceV0_ANPGetValue ((NPNVariable)1005) -#define kTypefaceInterfaceV0_ANPGetValue ((NPNVariable)1006) -#define kWindowInterfaceV0_ANPGetValue ((NPNVariable)1007) -#define kBitmapInterfaceV0_ANPGetValue ((NPNVariable)1008) -#define kSurfaceInterfaceV0_ANPGetValue ((NPNVariable)1009) -#define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010) -#define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011) - -#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012) -#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013) -#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014) -#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015) -#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016) -#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017) -#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018) -#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019) -#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020) - -/** queries for the drawing models supported on this device. - - NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits) - */ -#define kSupportedDrawingModel_ANPGetValue ((NPNVariable)2000) - -/** queries for the context (android.content.Context) of the plugin. If no - instance is specified the application's context is returned. If the instance - is given then the context returned is identical to the context used to - create the webview in which that instance resides. - - NOTE: Holding onto a non-application context after your instance has been - destroyed will cause a memory leak. Refer to the android documentation to - determine what context is best suited for your particular scenario. - - NPN_GetValue(inst, kJavaContext_ANPGetValue, jobject context) - */ -#define kJavaContext_ANPGetValue ((NPNVariable)2001) - -/////////////////////////////////////////////////////////////////////////////// -// NPN_SetValue - -/** Request to set the drawing model. SetValue will return false if the drawing - model is not supported or has insufficient information for configuration. - - NPN_SetValue(inst, kRequestDrawingModel_ANPSetValue, (void*)foo_ANPDrawingModel) - */ -#define kRequestDrawingModel_ANPSetValue ((NPPVariable)1000) - -/** These are used as bitfields in ANPSupportedDrawingModels_EnumValue, - and as-is in ANPRequestDrawingModel_EnumValue. The drawing model determines - how to interpret the ANPDrawingContext provided in the Draw event and how - to interpret the NPWindow->window field. - */ -enum ANPDrawingModels { - /** Draw into a bitmap from the browser thread in response to a Draw event. - NPWindow->window is reserved (ignore) - */ - kBitmap_ANPDrawingModel = 1 << 0, - /** Draw into a surface (e.g. raster, openGL, etc.) using the Java surface - interface. When this model is used the browser will invoke the Java - class specified in the plugin's apk manifest. From that class the browser - will invoke the appropriate method to return an an instance of a android - Java View. The instance is then embedded in the html. The plugin can then - manipulate the view as it would any normal Java View in android. - - Unlike the bitmap model, a surface model is opaque so no html content - behind the plugin will be visible. Unless the plugin needs to be - transparent the surface model should be chosen over the bitmap model as - it will have better performance. - - Further, a plugin can manipulate some surfaces in native code using the - ANPSurfaceInterface. This interface can be used to manipulate Java - objects that extend Surface.class by allowing them to access the - surface's underlying bitmap in native code. For instance, if a raster - surface is used the plugin can lock, draw directly into the bitmap, and - unlock the surface in native code without making JNI calls to the Java - surface object. - */ - kSurface_ANPDrawingModel = 1 << 1, - kOpenGL_ANPDrawingModel = 1 << 2, -}; -typedef int32_t ANPDrawingModel; - -/** Request to receive/disable events. If the pointer is NULL then all flags will - be disabled. Otherwise, the event type will be enabled iff its corresponding - bit in the EventFlags bit field is set. - - NPN_SetValue(inst, ANPAcceptEvents, (void*)EventFlags) - */ -#define kAcceptEvents_ANPSetValue ((NPPVariable)1001) - -/** The EventFlags are a set of bits used to determine which types of events the - plugin wishes to receive. For example, if the value is 0x03 then both key - and touch events will be provided to the plugin. - */ -enum ANPEventFlag { - kKey_ANPEventFlag = 0x01, - kTouch_ANPEventFlag = 0x02, -}; -typedef uint32_t ANPEventFlags; - -/////////////////////////////////////////////////////////////////////////////// -// NPP_GetValue - -/** Requests that the plugin return a java surface to be displayed. This will - only be used if the plugin has choosen the kSurface_ANPDrawingModel. - - NPP_GetValue(inst, kJavaSurface_ANPGetValue, jobject surface) - */ -#define kJavaSurface_ANPGetValue ((NPPVariable)2000) - - -/////////////////////////////////////////////////////////////////////////////// -// ANDROID INTERFACE DEFINITIONS - -/** Interfaces provide additional functionality to the plugin via function ptrs. - Once an interface is retrieved, it is valid for the lifetime of the plugin - (just like browserfuncs). - - All ANPInterfaces begin with an inSize field, which must be set by the - caller (plugin) with the number of bytes allocated for the interface. - e.g. SomeInterface si; si.inSize = sizeof(si); browser->getvalue(..., &si); - */ -struct ANPInterface { - uint32_t inSize; // size (in bytes) of this struct -}; - -enum ANPLogTypes { - kError_ANPLogType = 0, // error - kWarning_ANPLogType = 1, // warning - kDebug_ANPLogType = 2 // debug only (informational) -}; -typedef int32_t ANPLogType; - -struct ANPLogInterfaceV0 : ANPInterface { - /** dumps printf messages to the log file - e.g. interface->log(instance, kWarning_ANPLogType, "value is %d", value); - */ - void (*log)(ANPLogType, const char format[], ...); -}; - -/** ANPColor is always defined to have the same packing on all platforms, and - it is always unpremultiplied. - - This is in contrast to 32bit format(s) in bitmaps, which are premultiplied, - and their packing may vary depending on the platform, hence the need for - ANPBitmapInterface::getPixelPacking() - */ -typedef uint32_t ANPColor; -#define ANPColor_ASHIFT 24 -#define ANPColor_RSHIFT 16 -#define ANPColor_GSHIFT 8 -#define ANPColor_BSHIFT 0 -#define ANP_MAKE_COLOR(a, r, g, b) \ - (((a) << ANPColor_ASHIFT) | \ - ((r) << ANPColor_RSHIFT) | \ - ((g) << ANPColor_GSHIFT) | \ - ((b) << ANPColor_BSHIFT)) - -enum ANPPaintFlag { - kAntiAlias_ANPPaintFlag = 1 << 0, - kFilterBitmap_ANPPaintFlag = 1 << 1, - kDither_ANPPaintFlag = 1 << 2, - kUnderlineText_ANPPaintFlag = 1 << 3, - kStrikeThruText_ANPPaintFlag = 1 << 4, - kFakeBoldText_ANPPaintFlag = 1 << 5, -}; -typedef uint32_t ANPPaintFlags; - -enum ANPPaintStyles { - kFill_ANPPaintStyle = 0, - kStroke_ANPPaintStyle = 1, - kFillAndStroke_ANPPaintStyle = 2 -}; -typedef int32_t ANPPaintStyle; - -enum ANPPaintCaps { - kButt_ANPPaintCap = 0, - kRound_ANPPaintCap = 1, - kSquare_ANPPaintCap = 2 -}; -typedef int32_t ANPPaintCap; - -enum ANPPaintJoins { - kMiter_ANPPaintJoin = 0, - kRound_ANPPaintJoin = 1, - kBevel_ANPPaintJoin = 2 -}; -typedef int32_t ANPPaintJoin; - -enum ANPPaintAligns { - kLeft_ANPPaintAlign = 0, - kCenter_ANPPaintAlign = 1, - kRight_ANPPaintAlign = 2 -}; -typedef int32_t ANPPaintAlign; - -enum ANPTextEncodings { - kUTF8_ANPTextEncoding = 0, - kUTF16_ANPTextEncoding = 1, -}; -typedef int32_t ANPTextEncoding; - -enum ANPTypefaceStyles { - kBold_ANPTypefaceStyle = 1 << 0, - kItalic_ANPTypefaceStyle = 1 << 1 -}; -typedef uint32_t ANPTypefaceStyle; - -typedef uint32_t ANPFontTableTag; - -struct ANPFontMetrics { - /** The greatest distance above the baseline for any glyph (will be <= 0) */ - float fTop; - /** The recommended distance above the baseline (will be <= 0) */ - float fAscent; - /** The recommended distance below the baseline (will be >= 0) */ - float fDescent; - /** The greatest distance below the baseline for any glyph (will be >= 0) */ - float fBottom; - /** The recommended distance to add between lines of text (will be >= 0) */ - float fLeading; -}; - -struct ANPTypefaceInterfaceV0 : ANPInterface { - /** Return a new reference to the typeface that most closely matches the - requested name and style. Pass null as the name to return - the default font for the requested style. Will never return null - - The 5 generic font names "serif", "sans-serif", "monospace", "cursive", - "fantasy" are recognized, and will be mapped to their logical font - automatically by this call. - - @param name May be NULL. The name of the font family. - @param style The style (normal, bold, italic) of the typeface. - @return reference to the closest-matching typeface. Caller must call - unref() when they are done with the typeface. - */ - ANPTypeface* (*createFromName)(const char name[], ANPTypefaceStyle); - - /** Return a new reference to the typeface that most closely matches the - requested typeface and specified Style. Use this call if you want to - pick a new style from the same family of the existing typeface. - If family is NULL, this selects from the default font's family. - - @param family May be NULL. The name of the existing type face. - @param s The style (normal, bold, italic) of the type face. - @return reference to the closest-matching typeface. Call must call - unref() when they are done. - */ - ANPTypeface* (*createFromTypeface)(const ANPTypeface* family, - ANPTypefaceStyle); - - /** Return the owner count of the typeface. A newly created typeface has an - owner count of 1. When the owner count is reaches 0, the typeface is - deleted. - */ - int32_t (*getRefCount)(const ANPTypeface*); - - /** Increment the owner count on the typeface - */ - void (*ref)(ANPTypeface*); - - /** Decrement the owner count on the typeface. When the count goes to 0, - the typeface is deleted. - */ - void (*unref)(ANPTypeface*); - - /** Return the style bits for the specified typeface - */ - ANPTypefaceStyle (*getStyle)(const ANPTypeface*); - - /** Some fonts are stored in files. If that is true for the fontID, then - this returns the byte length of the full file path. If path is not null, - then the full path is copied into path (allocated by the caller), up to - length bytes. If index is not null, then it is set to the truetype - collection index for this font, or 0 if the font is not in a collection. - - Note: getFontPath does not assume that path is a null-terminated string, - so when it succeeds, it only copies the bytes of the file name and - nothing else (i.e. it copies exactly the number of bytes returned by the - function. If the caller wants to treat path[] as a C string, it must be - sure that it is allocated at least 1 byte larger than the returned size, - and it must copy in the terminating 0. - - If the fontID does not correspond to a file, then the function returns - 0, and the path and index parameters are ignored. - - @param fontID The font whose file name is being queried - @param path Either NULL, or storage for receiving up to length bytes - of the font's file name. Allocated by the caller. - @param length The maximum space allocated in path (by the caller). - Ignored if path is NULL. - @param index Either NULL, or receives the TTC index for this font. - If the font is not a TTC, then will be set to 0. - @return The byte length of th font's file name, or 0 if the font is not - baked by a file. - */ - int32_t (*getFontPath)(const ANPTypeface*, char path[], int32_t length, - int32_t* index); - - /** Return a UTF8 encoded path name for the font directory, or NULL if not - supported. If returned, this string address will be valid for the life - of the plugin instance. It will always end with a '/' character. - */ - const char* (*getFontDirectoryPath)(); -}; - -struct ANPPaintInterfaceV0 : ANPInterface { - /** Return a new paint object, which holds all of the color and style - attributes that affect how things (geometry, text, bitmaps) are drawn - in a ANPCanvas. - - The paint that is returned is not tied to any particular plugin - instance, but it must only be accessed from one thread at a time. - */ - ANPPaint* (*newPaint)(); - void (*deletePaint)(ANPPaint*); - - ANPPaintFlags (*getFlags)(const ANPPaint*); - void (*setFlags)(ANPPaint*, ANPPaintFlags); - - ANPColor (*getColor)(const ANPPaint*); - void (*setColor)(ANPPaint*, ANPColor); - - ANPPaintStyle (*getStyle)(const ANPPaint*); - void (*setStyle)(ANPPaint*, ANPPaintStyle); - - float (*getStrokeWidth)(const ANPPaint*); - float (*getStrokeMiter)(const ANPPaint*); - ANPPaintCap (*getStrokeCap)(const ANPPaint*); - ANPPaintJoin (*getStrokeJoin)(const ANPPaint*); - void (*setStrokeWidth)(ANPPaint*, float); - void (*setStrokeMiter)(ANPPaint*, float); - void (*setStrokeCap)(ANPPaint*, ANPPaintCap); - void (*setStrokeJoin)(ANPPaint*, ANPPaintJoin); - - ANPTextEncoding (*getTextEncoding)(const ANPPaint*); - ANPPaintAlign (*getTextAlign)(const ANPPaint*); - float (*getTextSize)(const ANPPaint*); - float (*getTextScaleX)(const ANPPaint*); - float (*getTextSkewX)(const ANPPaint*); - void (*setTextEncoding)(ANPPaint*, ANPTextEncoding); - void (*setTextAlign)(ANPPaint*, ANPPaintAlign); - void (*setTextSize)(ANPPaint*, float); - void (*setTextScaleX)(ANPPaint*, float); - void (*setTextSkewX)(ANPPaint*, float); - - /** Return the typeface ine paint, or null if there is none. This does not - modify the owner count of the returned typeface. - */ - ANPTypeface* (*getTypeface)(const ANPPaint*); - - /** Set the paint's typeface. If the paint already had a non-null typeface, - its owner count is decremented. If the new typeface is non-null, its - owner count is incremented. - */ - void (*setTypeface)(ANPPaint*, ANPTypeface*); - - /** Return the width of the text. If bounds is not null, return the bounds - of the text in that rectangle. - */ - float (*measureText)(ANPPaint*, const void* text, uint32_t byteLength, - ANPRectF* bounds); - - /** Return the number of unichars specifed by the text. - If widths is not null, returns the array of advance widths for each - unichar. - If bounds is not null, returns the array of bounds for each unichar. - */ - int (*getTextWidths)(ANPPaint*, const void* text, uint32_t byteLength, - float widths[], ANPRectF bounds[]); - - /** Return in metrics the spacing values for text, respecting the paint's - typeface and pointsize, and return the spacing between lines - (descent - ascent + leading). If metrics is NULL, it will be ignored. - */ - float (*getFontMetrics)(ANPPaint*, ANPFontMetrics* metrics); -}; - -struct ANPCanvasInterfaceV0 : ANPInterface { - /** Return a canvas that will draw into the specified bitmap. Note: the - canvas copies the fields of the bitmap, so it need not persist after - this call, but the canvas DOES point to the same pixel memory that the - bitmap did, so the canvas should not be used after that pixel memory - goes out of scope. In the case of creating a canvas to draw into the - pixels provided by kDraw_ANPEventType, those pixels are only while - handling that event. - - The canvas that is returned is not tied to any particular plugin - instance, but it must only be accessed from one thread at a time. - */ - ANPCanvas* (*newCanvas)(const ANPBitmap*); - void (*deleteCanvas)(ANPCanvas*); - - void (*save)(ANPCanvas*); - void (*restore)(ANPCanvas*); - void (*translate)(ANPCanvas*, float tx, float ty); - void (*scale)(ANPCanvas*, float sx, float sy); - void (*rotate)(ANPCanvas*, float degrees); - void (*skew)(ANPCanvas*, float kx, float ky); - void (*concat)(ANPCanvas*, const ANPMatrix*); - void (*clipRect)(ANPCanvas*, const ANPRectF*); - void (*clipPath)(ANPCanvas*, const ANPPath*); - - /** Return the current matrix on the canvas - */ - void (*getTotalMatrix)(ANPCanvas*, ANPMatrix*); - /** Return the current clip bounds in local coordinates, expanding it to - account for antialiasing edge effects if aa is true. If the - current clip is empty, return false and ignore the bounds argument. - */ - bool (*getLocalClipBounds)(ANPCanvas*, ANPRectF* bounds, bool aa); - /** Return the current clip bounds in device coordinates in bounds. If the - current clip is empty, return false and ignore the bounds argument. - */ - bool (*getDeviceClipBounds)(ANPCanvas*, ANPRectI* bounds); - - void (*drawColor)(ANPCanvas*, ANPColor); - void (*drawPaint)(ANPCanvas*, const ANPPaint*); - void (*drawLine)(ANPCanvas*, float x0, float y0, float x1, float y1, - const ANPPaint*); - void (*drawRect)(ANPCanvas*, const ANPRectF*, const ANPPaint*); - void (*drawOval)(ANPCanvas*, const ANPRectF*, const ANPPaint*); - void (*drawPath)(ANPCanvas*, const ANPPath*, const ANPPaint*); - void (*drawText)(ANPCanvas*, const void* text, uint32_t byteLength, - float x, float y, const ANPPaint*); - void (*drawPosText)(ANPCanvas*, const void* text, uint32_t byteLength, - const float xy[], const ANPPaint*); - void (*drawBitmap)(ANPCanvas*, const ANPBitmap*, float x, float y, - const ANPPaint*); - void (*drawBitmapRect)(ANPCanvas*, const ANPBitmap*, - const ANPRectI* src, const ANPRectF* dst, - const ANPPaint*); -}; - -struct ANPWindowInterfaceV0 : ANPInterface { - /** Registers a set of rectangles that the plugin would like to keep on - screen. The rectangles are listed in order of priority with the highest - priority rectangle in location rects[0]. The browser will attempt to keep - as many of the rectangles on screen as possible and will scroll them into - view in response to the invocation of this method and other various events. - The count specifies how many rectangles are in the array. If the count is - zero it signals the browser that any existing rectangles should be cleared - and no rectangles will be tracked. - */ - void (*setVisibleRects)(NPP instance, const ANPRectI rects[], int32_t count); - /** Clears any rectangles that are being tracked as a result of a call to - setVisibleRects. This call is equivalent to setVisibleRect(inst, NULL, 0). - */ - void (*clearVisibleRects)(NPP instance); - /** Given a boolean value of true the device will be requested to provide - a keyboard. A value of false will result in a request to hide the - keyboard. Further, the on-screen keyboard will not be displayed if a - physical keyboard is active. - */ - void (*showKeyboard)(NPP instance, bool value); - /** Called when a plugin wishes to enter into full screen mode. The plugin's - Java class (defined in the plugin's apk manifest) will be called - asynchronously to provide a View object to be displayed full screen. - */ - void (*requestFullScreen)(NPP instance); - /** Called when a plugin wishes to exit from full screen mode. As a result, - the plugin's full screen view will be discarded by the view system. - */ - void (*exitFullScreen)(NPP instance); - /** Called when a plugin wishes to be zoomed and centered in the current view. - */ - void (*requestCenterFitZoom)(NPP instance); -}; - -enum ANPScreenOrientations { - /** No preference specified: let the system decide the best orientation. - */ - kDefault_ANPScreenOrientation = 0, - /** Would like to have the screen in a landscape orientation, but it will - not allow for 180 degree rotations. - */ - kFixedLandscape_ANPScreenOrientation = 1, - /** Would like to have the screen in a portrait orientation, but it will - not allow for 180 degree rotations. - */ - kFixedPortrait_ANPScreenOrientation = 2, - /** Would like to have the screen in landscape orientation, but can use the - sensor to change which direction the screen is facing. - */ - kLandscape_ANPScreenOrientation = 3, - /** Would like to have the screen in portrait orientation, but can use the - sensor to change which direction the screen is facing. - */ - kPortrait_ANPScreenOrientation = 4 -}; - -typedef int32_t ANPScreenOrientation; - -struct ANPWindowInterfaceV2 : ANPWindowInterfaceV0 { - /** Returns a rectangle representing the visible area of the plugin on - screen. The coordinates are relative to the size of the plugin in the - document and therefore will never be negative or exceed the plugin's size. - */ - ANPRectI (*visibleRect)(NPP instance); - - /** Called when the plugin wants to specify a particular screen orientation - when entering into full screen mode. The orientation must be set prior - to entering into full screen. After entering full screen any subsequent - changes will be updated the next time the plugin goes full screen. - */ - void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation); -}; - -/////////////////////////////////////////////////////////////////////////////// - -enum ANPSampleFormats { - kUnknown_ANPSamleFormat = 0, - kPCM16Bit_ANPSampleFormat = 1, - kPCM8Bit_ANPSampleFormat = 2 -}; -typedef int32_t ANPSampleFormat; - -/** The audio buffer is passed to the callback proc to request more samples. - It is owned by the system, and the callback may read it, but should not - maintain a pointer to it outside of the scope of the callback proc. - */ -struct ANPAudioBuffer { - // RO - repeat what was specified in newTrack() - int32_t channelCount; - // RO - repeat what was specified in newTrack() - ANPSampleFormat format; - /** This buffer is owned by the caller. Inside the callback proc, up to - "size" bytes of sample data should be written into this buffer. The - address is only valid for the scope of a single invocation of the - callback proc. - */ - void* bufferData; - /** On input, specifies the maximum number of bytes that can be written - to "bufferData". On output, specifies the actual number of bytes that - the callback proc wrote into "bufferData". - */ - uint32_t size; -}; - -enum ANPAudioEvents { - /** This event is passed to the callback proc when the audio-track needs - more sample data written to the provided buffer parameter. - */ - kMoreData_ANPAudioEvent = 0, - /** This event is passed to the callback proc if the audio system runs out - of sample data. In this event, no buffer parameter will be specified - (i.e. NULL will be passed to the 3rd parameter). - */ - kUnderRun_ANPAudioEvent = 1 -}; -typedef int32_t ANPAudioEvent; - -/** Called to feed sample data to the track. This will be called in a separate - thread. However, you may call trackStop() from the callback (but you - cannot delete the track). - - For example, when you have written the last chunk of sample data, you can - immediately call trackStop(). This will take effect after the current - buffer has been played. - - The "user" parameter is the same value that was passed to newTrack() - */ -typedef void (*ANPAudioCallbackProc)(ANPAudioEvent event, void* user, - ANPAudioBuffer* buffer); - -struct ANPAudioTrack; // abstract type for audio tracks - -struct ANPAudioTrackInterfaceV0 : ANPInterface { - /** Create a new audio track, or NULL on failure. The track is initially in - the stopped state and therefore ANPAudioCallbackProc will not be called - until the track is started. - */ - ANPAudioTrack* (*newTrack)(uint32_t sampleRate, // sampling rate in Hz - ANPSampleFormat, - int channelCount, // MONO=1, STEREO=2 - ANPAudioCallbackProc, - void* user); - /** Deletes a track that was created using newTrack. The track can be - deleted in any state and it waits for the ANPAudioCallbackProc thread - to exit before returning. - */ - void (*deleteTrack)(ANPAudioTrack*); - - void (*start)(ANPAudioTrack*); - void (*pause)(ANPAudioTrack*); - void (*stop)(ANPAudioTrack*); - /** Returns true if the track is not playing (e.g. pause or stop was called, - or start was never called. - */ - bool (*isStopped)(ANPAudioTrack*); -}; - -struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 { - /** Returns the track's latency in milliseconds. */ - uint32_t (*trackLatency)(ANPAudioTrack*); -}; - - -/////////////////////////////////////////////////////////////////////////////// -// DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent - -enum ANPEventTypes { - kNull_ANPEventType = 0, - kKey_ANPEventType = 1, - /** Mouse events are triggered by either clicking with the navigational pad - or by tapping the touchscreen (if the kDown_ANPTouchAction is handled by - the plugin then no mouse event is generated). The kKey_ANPEventFlag has - to be set to true in order to receive these events. - */ - kMouse_ANPEventType = 2, - /** Touch events are generated when the user touches on the screen. The - kTouch_ANPEventFlag has to be set to true in order to receive these - events. - */ - kTouch_ANPEventType = 3, - /** Only triggered by a plugin using the kBitmap_ANPDrawingModel. This event - signals that the plugin needs to redraw itself into the provided bitmap. - */ - kDraw_ANPEventType = 4, - kLifecycle_ANPEventType = 5, - - /** This event type is completely defined by the plugin. - When creating an event, the caller must always set the first - two fields, the remaining data is optional. - ANPEvent evt; - evt.inSize = sizeof(ANPEvent); - evt.eventType = kCustom_ANPEventType - // other data slots are optional - evt.other[] = ...; - To post a copy of the event, call - eventInterface->postEvent(myNPPInstance, &evt); - That call makes a copy of the event struct, and post that on the event - queue for the plugin. - */ - kCustom_ANPEventType = 6, -}; -typedef int32_t ANPEventType; - -enum ANPKeyActions { - kDown_ANPKeyAction = 0, - kUp_ANPKeyAction = 1, -}; -typedef int32_t ANPKeyAction; - -#include "ANPKeyCodes.h" -typedef int32_t ANPKeyCode; - -enum ANPKeyModifiers { - kAlt_ANPKeyModifier = 1 << 0, - kShift_ANPKeyModifier = 1 << 1, -}; -// bit-field containing some number of ANPKeyModifier bits -typedef uint32_t ANPKeyModifier; - -enum ANPMouseActions { - kDown_ANPMouseAction = 0, - kUp_ANPMouseAction = 1, -}; -typedef int32_t ANPMouseAction; - -enum ANPTouchActions { - /** This occurs when the user first touches on the screen. As such, this - action will always occur prior to any of the other touch actions. If - the plugin chooses to not handle this action then no other events - related to that particular touch gesture will be generated. - */ - kDown_ANPTouchAction = 0, - kUp_ANPTouchAction = 1, - kMove_ANPTouchAction = 2, - kCancel_ANPTouchAction = 3, - // The web view will ignore the return value from the following actions - kLongPress_ANPTouchAction = 4, - kDoubleTap_ANPTouchAction = 5, -}; -typedef int32_t ANPTouchAction; - -enum ANPLifecycleActions { - /** The web view containing this plugin has been paused. See documentation - on the android activity lifecycle for more information. - */ - kPause_ANPLifecycleAction = 0, - /** The web view containing this plugin has been resumed. See documentation - on the android activity lifecycle for more information. - */ - kResume_ANPLifecycleAction = 1, - /** The plugin has focus and is now the recipient of input events (e.g. key, - touch, etc.) - */ - kGainFocus_ANPLifecycleAction = 2, - /** The plugin has lost focus and will not receive any input events until it - regains focus. This event is always preceded by a GainFocus action. - */ - kLoseFocus_ANPLifecycleAction = 3, - /** The browser is running low on available memory and is requesting that - the plugin free any unused/inactive resources to prevent a performance - degradation. - */ - kFreeMemory_ANPLifecycleAction = 4, - /** The page has finished loading. This happens when the page's top level - frame reports that it has completed loading. - */ - kOnLoad_ANPLifecycleAction = 5, - /** The browser is honoring the plugin's request to go full screen. Upon - returning from this event the browser will resize the plugin's java - surface to full-screen coordinates. - */ - kEnterFullScreen_ANPLifecycleAction = 6, - /** The browser has exited from full screen mode. Immediately prior to - sending this event the browser has resized the plugin's java surface to - its original coordinates. - */ - kExitFullScreen_ANPLifecycleAction = 7, - /** The plugin is visible to the user on the screen. This event will always - occur after a kOffScreen_ANPLifecycleAction event. - */ - kOnScreen_ANPLifecycleAction = 8, - /** The plugin is no longer visible to the user on the screen. This event - will always occur prior to an kOnScreen_ANPLifecycleAction event. - */ - kOffScreen_ANPLifecycleAction = 9, -}; -typedef uint32_t ANPLifecycleAction; - -/* This is what is passed to NPP_HandleEvent() */ -struct ANPEvent { - uint32_t inSize; // size of this struct in bytes - ANPEventType eventType; - // use based on the value in eventType - union { - struct { - ANPKeyAction action; - ANPKeyCode nativeCode; - int32_t virtualCode; // windows virtual key code - ANPKeyModifier modifiers; - int32_t repeatCount; // 0 for initial down (or up) - int32_t unichar; // 0 if there is no value - } key; - struct { - ANPMouseAction action; - int32_t x; // relative to your "window" (0...width) - int32_t y; // relative to your "window" (0...height) - } mouse; - struct { - ANPTouchAction action; - ANPKeyModifier modifiers; - int32_t x; // relative to your "window" (0...width) - int32_t y; // relative to your "window" (0...height) - } touch; - struct { - ANPLifecycleAction action; - } lifecycle; - struct { - ANPDrawingModel model; - // relative to (0,0) in top-left of your plugin - ANPRectI clip; - // use based on the value in model - union { - ANPBitmap bitmap; - struct { - int32_t width; - int32_t height; - } surfaceSize; - } data; - } draw; - } data; -}; - - -struct ANPEventInterfaceV0 : ANPInterface { - /** Post a copy of the specified event to the plugin. The event will be - delivered to the plugin in its main thread (the thread that receives - other ANPEvents). If, after posting before delivery, the NPP instance - is torn down, the event will be discarded. - */ - void (*postEvent)(NPP inst, const ANPEvent* event); -}; - -struct ANPSurfaceInterfaceV0 : ANPInterface { - /** Locks the surface from manipulation by other threads and provides a bitmap - to be written to. The dirtyRect param specifies which portion of the - bitmap will be written to. If the dirtyRect is NULL then the entire - surface will be considered dirty. If the lock was successful the function - will return true and the bitmap will be set to point to a valid bitmap. - If not the function will return false and the bitmap will be set to NULL. - */ - bool (*lock)(JNIEnv* env, jobject surface, ANPBitmap* bitmap, ANPRectI* dirtyRect); - /** Given a locked surface handle (i.e. result of a successful call to lock) - the surface is unlocked and the contents of the bitmap, specifically - those inside the dirtyRect are written to the screen. - */ - void (*unlock)(JNIEnv* env, jobject surface); -}; - -/** - * TODO should we not use EGL and GL data types for ABI safety? - */ -struct ANPTextureInfo { - GLuint textureId; - uint32_t width; - uint32_t height; - GLenum internalFormat; -}; - -typedef void* ANPEGLContext; - -struct ANPOpenGLInterfaceV0 : ANPInterface { - ANPEGLContext (*acquireContext)(NPP instance); - - ANPTextureInfo (*lockTexture)(NPP instance); - - void (*releaseTexture)(NPP instance, const ANPTextureInfo*); - - /** - * Invert the contents of the plugin on the y-axis. - * default is to not be inverted (i.e. use OpenGL coordinates) - */ - void (*invertPluginContent)(NPP instance, bool isContentInverted); -}; - -enum ANPPowerStates { - kDefault_ANPPowerState = 0, - kScreenOn_ANPPowerState = 1 -}; -typedef int32_t ANPPowerState; - -struct ANPSystemInterfaceV1 : ANPInterface { - /** Return the path name for the current Application's plugin data directory, - or NULL if not supported - */ - const char* (*getApplicationDataDirectory)(); - - /** A helper function to load java classes from the plugin's apk. The - function looks for a class given the fully qualified and null terminated - string representing the className. For example, - - const char* className = "com.android.mypackage.MyClass"; - - If the class cannot be found or there is a problem loading the class - NULL will be returned. - */ - jclass (*loadJavaClass)(NPP instance, const char* className); - - void (*setPowerState)(NPP instance, ANPPowerState powerState); -}; - -struct ANPSystemInterfaceV2 : ANPInterface { - /** Return the path name for the current Application's plugin data directory, - or NULL if not supported. This directory will change depending on whether - or not the plugin is found within an incognito tab. - */ - const char* (*getApplicationDataDirectory)(NPP instance); - - // redeclaration of existing features - jclass (*loadJavaClass)(NPP instance, const char* className); - void (*setPowerState)(NPP instance, ANPPowerState powerState); -}; - -typedef void* ANPNativeWindow; - -/** Called to notify the plugin that a video frame has been composited by the -* browser for display. This will be called in a separate thread and as such -* you cannot call releaseNativeWindow from the callback. -* -* The timestamp is in nanoseconds, and is monotonically increasing. -*/ -typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp); - -struct ANPVideoInterfaceV1 : ANPInterface { - - /** - * Constructs a new native window to be used for rendering video content. - * - * Subsequent calls will produce new windows, but may also return NULL after - * n attempts if the browser has reached it's limit. Further, if the browser - * is unable to acquire the window quickly it may also return NULL in order - * to not prevent the plugin from executing. A subsequent call will then - * return the window if it is avaiable. - * - * NOTE: The hardware may fail if you try to decode more than the allowable - * number of videos supported on that device. - */ - ANPNativeWindow (*acquireNativeWindow)(NPP instance); - - /** - * Sets the rectangle that specifies where the video content is to be drawn. - * The dimensions are in document space. Further, if the rect is NULL the - * browser will not attempt to draw the window, therefore do not set the - * dimensions until you queue the first buffer in the window. - */ - void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions); - - /** - */ - void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window); - - /** Set a callback to be notified when an ANPNativeWindow is composited by - * the browser. - */ - void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc); -}; - -struct ANPNativeWindowInterfaceV0 : ANPInterface { - /** - * Constructs a new native window to be used for rendering plugin content. - * - * Subsequent calls will return the original constructed window. Further, if - * the browser is unable to acquire the window quickly it may return NULL in - * order to not block the plugin indefinitely. A subsequent call will then - * return the window if it is available. - */ - ANPNativeWindow (*acquireNativeWindow)(NPP instance); - - /** - * Invert the contents of the plugin on the y-axis. - * default is to not be inverted (e.g. use OpenGL coordinates) - */ - void (*invertPluginContent)(NPP instance, bool isContentInverted); -}; - - -#endif diff --git a/dom/plugins/base/android/moz.build b/dom/plugins/base/android/moz.build deleted file mode 100644 index d95d20d5b..000000000 --- a/dom/plugins/base/android/moz.build +++ /dev/null @@ -1,35 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'android_npapi.h', - 'ANPKeyCodes.h', -] - -SOURCES += [ - 'ANPAudio.cpp', - 'ANPEvent.cpp', - 'ANPLog.cpp', - 'ANPNativeWindow.cpp', - 'ANPSurface.cpp', - 'ANPSystem.cpp', - 'ANPVideo.cpp', - 'ANPWindow.cpp', -] - -include('/ipc/chromium/chromium-config.mozbuild') - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/dom/plugins/base', - '/gfx/gl', - '/widget', - '/widget/android', -] - -DEFINES['MOZ_APP_NAME'] = '"%s"' % CONFIG['MOZ_APP_NAME'] - -CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/dom/plugins/base/moz.build b/dom/plugins/base/moz.build index f43f75f79..a159d6fc1 100644 --- a/dom/plugins/base/moz.build +++ b/dom/plugins/base/moz.build @@ -4,9 +4,6 @@ # 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/. -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - DIRS += ['android'] - XPIDL_SOURCES += [ 'nsIHTTPHeaderListener.idl', 'nsIPluginDocument.idl', @@ -89,16 +86,10 @@ LOCAL_INCLUDES += [ '/layout/xul', '/netwerk/base', '/widget', - '/widget/android', '/widget/cocoa', '/xpcom/base', ] -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - LOCAL_INCLUDES += [ - '/dom/plugins/base/android', - ] - if CONFIG['OS_ARCH'] == 'WINNT': LOCAL_INCLUDES += [ '/xpcom/base', @@ -106,8 +97,6 @@ if CONFIG['OS_ARCH'] == 'WINNT': include('/ipc/chromium/chromium-config.mozbuild') -DEFINES['SK_BUILD_FOR_ANDROID_NDK'] = True - FINAL_LIBRARY = 'xul' CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/dom/plugins/base/npfunctions.h b/dom/plugins/base/npfunctions.h index 4c9d66ea0..73097d115 100644 --- a/dom/plugins/base/npfunctions.h +++ b/dom/plugins/base/npfunctions.h @@ -9,10 +9,6 @@ #include "npapi.h" #include "npruntime.h" -#ifdef MOZ_WIDGET_ANDROID -#include <jni.h> -#endif - typedef NPError (* NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); typedef NPError (* NPP_DestroyProcPtr)(NPP instance, NPSavedData** save); typedef NPError (* NPP_SetWindowProcPtr)(NPP instance, NPWindow* window); @@ -261,14 +257,9 @@ NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs); typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*); NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs); #else -#ifdef MOZ_WIDGET_ANDROID -typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*, JNIEnv* pEnv); -NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, JNIEnv* pEnv); -#else typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*); NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs); #endif -#endif typedef NPError (*NP_ShutdownFunc)(void); NP_EXPORT(NPError) NP_Shutdown(void); typedef NPError (*NP_GetValueFunc)(void *, NPPVariable, void *); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 697bfacd4..da4f09914 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -97,15 +97,6 @@ using mozilla::plugins::PluginModuleContentParent; #endif #endif -#ifdef MOZ_WIDGET_ANDROID -#include <android/log.h> -#include "android_npapi.h" -#include "ANPBase.h" -#include "GeneratedJNIWrappers.h" -#undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#endif - #include "nsIAudioChannelAgent.h" #include "AudioChannelService.h" @@ -236,11 +227,7 @@ nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID, bool nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag) { -#ifdef MOZ_WIDGET_ANDROID - return false; -#else return true; -#endif } inline PluginLibrary* @@ -282,7 +269,7 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult) return NS_ERROR_FAILURE; } -#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) +#ifdef XP_MACOSX if (!pluginLib->HasRequiredFunctions()) { NS_WARNING("Not all necessary functions exposed by plugin, it will not load."); delete pluginLib; @@ -2019,156 +2006,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result) return NPERR_NO_ERROR; } -#ifdef MOZ_WIDGET_ANDROID - case kLogInterfaceV0_ANPGetValue: { - LOG("get log interface"); - ANPLogInterfaceV0 *i = (ANPLogInterfaceV0 *) result; - InitLogInterface(i); - return NPERR_NO_ERROR; - } - - case kBitmapInterfaceV0_ANPGetValue: { - LOG("get bitmap interface"); - return NPERR_GENERIC_ERROR; - } - - case kMatrixInterfaceV0_ANPGetValue: { - LOG("get matrix interface"); - return NPERR_GENERIC_ERROR; - } - - case kPathInterfaceV0_ANPGetValue: { - LOG("get path interface"); - return NPERR_GENERIC_ERROR; - } - - case kTypefaceInterfaceV0_ANPGetValue: { - LOG("get typeface interface"); - ANPTypefaceInterfaceV0 *i = (ANPTypefaceInterfaceV0 *) result; - InitTypeFaceInterface(i); - return NPERR_NO_ERROR; - } - - case kPaintInterfaceV0_ANPGetValue: { - LOG("get paint interface"); - ANPPaintInterfaceV0 *i = (ANPPaintInterfaceV0 *) result; - InitPaintInterface(i); - return NPERR_NO_ERROR; - } - - case kCanvasInterfaceV0_ANPGetValue: { - LOG("get canvas interface"); - ANPCanvasInterfaceV0 *i = (ANPCanvasInterfaceV0 *) result; - InitCanvasInterface(i); - return NPERR_NO_ERROR; - } - - case kWindowInterfaceV0_ANPGetValue: { - LOG("get window interface"); - ANPWindowInterfaceV0 *i = (ANPWindowInterfaceV0 *) result; - InitWindowInterface(i); - return NPERR_NO_ERROR; - } - - case kAudioTrackInterfaceV0_ANPGetValue: { - LOG("get audio interface"); - ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result; - InitAudioTrackInterfaceV0(i); - return NPERR_NO_ERROR; - } - - case kEventInterfaceV0_ANPGetValue: { - LOG("get event interface"); - ANPEventInterfaceV0 *i = (ANPEventInterfaceV0 *) result; - InitEventInterface(i); - return NPERR_NO_ERROR; - } - - case kSystemInterfaceV0_ANPGetValue: { - LOG("get system interface"); - return NPERR_GENERIC_ERROR; - } - - case kSurfaceInterfaceV0_ANPGetValue: { - LOG("get surface interface"); - ANPSurfaceInterfaceV0 *i = (ANPSurfaceInterfaceV0 *) result; - InitSurfaceInterface(i); - return NPERR_NO_ERROR; - } - - case kSupportedDrawingModel_ANPGetValue: { - LOG("get supported drawing model"); - return NPERR_GENERIC_ERROR; - } - - case kJavaContext_ANPGetValue: { - LOG("get java context"); - auto ret = java::GeckoAppShell::GetContext(); - if (!ret) - return NPERR_GENERIC_ERROR; - - *static_cast<jobject*>(result) = ret.Forget(); - return NPERR_NO_ERROR; - } - - case kAudioTrackInterfaceV1_ANPGetValue: { - LOG("get audio interface v1"); - ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result; - InitAudioTrackInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kNativeWindowInterfaceV0_ANPGetValue: { - LOG("get native window interface v0"); - ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result; - InitNativeWindowInterface(i); - return NPERR_NO_ERROR; - } - - case kOpenGLInterfaceV0_ANPGetValue: { - LOG("get openGL interface"); - return NPERR_GENERIC_ERROR; - } - - case kWindowInterfaceV1_ANPGetValue: { - LOG("get Window interface V1"); - return NPERR_GENERIC_ERROR; - } - - case kWindowInterfaceV2_ANPGetValue: { - LOG("get Window interface V2"); - ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result; - InitWindowInterfaceV2(i); - return NPERR_NO_ERROR; - } - - case kVideoInterfaceV0_ANPGetValue: { - LOG("get video interface V0"); - return NPERR_GENERIC_ERROR; - } - - case kVideoInterfaceV1_ANPGetValue: { - LOG("get video interface V1"); - ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result; - InitVideoInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kSystemInterfaceV1_ANPGetValue: { - LOG("get system interface v1"); - ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(result); - InitSystemInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kSystemInterfaceV2_ANPGetValue: { - LOG("get system interface v2"); - ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(result); - InitSystemInterfaceV2(i); - return NPERR_NO_ERROR; - } -#endif - // we no longer hand out any XPCOM objects case NPNVDOMElement: case NPNVDOMWindow: @@ -2298,8 +2135,6 @@ _setvalue(NPP npp, NPPVariable variable, void *result) return NPERR_NO_ERROR; } -#ifndef MOZ_WIDGET_ANDROID - // On android, their 'drawing model' uses the same constant! case NPPVpluginDrawingModel: { if (inst) { inst->SetDrawingModel((NPDrawingModel)NS_PTR_TO_INT32(result)); @@ -2309,7 +2144,6 @@ _setvalue(NPP npp, NPPVariable variable, void *result) return NPERR_GENERIC_ERROR; } } -#endif #ifdef XP_MACOSX case NPPVpluginEventModel: { @@ -2322,14 +2156,7 @@ _setvalue(NPP npp, NPPVariable variable, void *result) } } #endif -#ifdef MOZ_WIDGET_ANDROID - case kRequestDrawingModel_ANPSetValue: - if (inst) - inst->SetANPDrawingModel(NS_PTR_TO_INT32(result)); - return NPERR_NO_ERROR; - case kAcceptEvents_ANPSetValue: - return NPERR_NO_ERROR; -#endif + default: return NPERR_GENERIC_ERROR; } @@ -2682,13 +2509,8 @@ _unscheduletimer(NPP instance, uint32_t timerID) return; } -#ifdef MOZ_WIDGET_ANDROID - // Sometimes Flash calls this with a dead NPP instance. Ensure the one we have - // here is valid and maps to a nsNPAPIPluginInstance. - nsNPAPIPluginInstance *inst = nsNPAPIPluginInstance::GetFromNPP(instance); -#else nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata; -#endif + if (!inst) return; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index 4b211ed7e..ce24b6296 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -5,11 +5,6 @@ #include "mozilla/DebugOnly.h" -#ifdef MOZ_WIDGET_ANDROID -// For ScreenOrientation.h and Hal.h -#include "base/basictypes.h" -#endif - #include "mozilla/Logging.h" #include "prmem.h" #include "nscore.h" @@ -45,64 +40,6 @@ using namespace mozilla; using namespace mozilla::dom; -#ifdef MOZ_WIDGET_ANDROID -#include "ANPBase.h" -#include <android/log.h> -#include "android_npapi.h" -#include "mozilla/Mutex.h" -#include "mozilla/CondVar.h" -#include "mozilla/dom/ScreenOrientation.h" -#include "mozilla/Hal.h" -#include "GLContextProvider.h" -#include "GLContext.h" -#include "TexturePoolOGL.h" -#include "SurfaceTypes.h" -#include "EGLUtils.h" - -using namespace mozilla; -using namespace mozilla::gl; - -typedef nsNPAPIPluginInstance::VideoInfo VideoInfo; - -class PluginEventRunnable : public Runnable -{ -public: - PluginEventRunnable(nsNPAPIPluginInstance* instance, ANPEvent* event) - : mInstance(instance), mEvent(*event), mCanceled(false) {} - - virtual nsresult Run() { - if (mCanceled) - return NS_OK; - - mInstance->HandleEvent(&mEvent, nullptr); - mInstance->PopPostedEvent(this); - return NS_OK; - } - - void Cancel() { mCanceled = true; } -private: - nsNPAPIPluginInstance* mInstance; - ANPEvent mEvent; - bool mCanceled; -}; - -static RefPtr<GLContext> sPluginContext = nullptr; - -static bool EnsureGLContext() -{ - if (!sPluginContext) { - const auto flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE; - nsCString discardedFailureId; - sPluginContext = GLContextProvider::CreateHeadless(flags, &discardedFailureId); - } - - return sPluginContext != nullptr; -} - -static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap; - -#endif - using namespace mozilla; using namespace mozilla::plugins::parent; using namespace mozilla::layers; @@ -113,13 +50,6 @@ NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback) nsNPAPIPluginInstance::nsNPAPIPluginInstance() : mDrawingModel(kDefaultDrawingModel) -#ifdef MOZ_WIDGET_ANDROID - , mANPDrawingModel(0) - , mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary) - , mWakeLocked(false) - , mFullScreen(false) - , mOriginPos(gl::OriginPos::TopLeft) -#endif , mRunning(NOT_STARTED) , mWindowless(false) , mTransparent(false) @@ -132,9 +62,6 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance() #ifdef XP_MACOSX , mCurrentPluginEvent(nullptr) #endif -#ifdef MOZ_WIDGET_ANDROID - , mOnScreen(true) -#endif , mHaveJavaC2PJSObjectQuirk(false) , mCachedParamLength(0) , mCachedParamNames(nullptr) @@ -145,20 +72,12 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance() mNPP.ndata = this; PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance ctor: this=%p\n",this)); - -#ifdef MOZ_WIDGET_ANDROID - sPluginNPPMap[&mNPP] = this; -#endif } nsNPAPIPluginInstance::~nsNPAPIPluginInstance() { PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance dtor: this=%p\n",this)); -#ifdef MOZ_WIDGET_ANDROID - sPluginNPPMap.erase(&mNPP); -#endif - if (mMIMEType) { PR_Free((void *)mMIMEType); mMIMEType = nullptr; @@ -195,21 +114,6 @@ nsNPAPIPluginInstance::Destroy() Stop(); mPlugin = nullptr; mAudioChannelAgent = nullptr; - -#if MOZ_WIDGET_ANDROID - if (mContentSurface) - mContentSurface->SetFrameAvailableCallback(nullptr); - - mContentSurface = nullptr; - - std::map<void*, VideoInfo*>::iterator it; - for (it = mVideos.begin(); it != mVideos.end(); it++) { - it->second->mSurfaceTexture->SetFrameAvailableCallback(nullptr); - delete it->second; - } - mVideos.clear(); - SetWakeLock(false); -#endif } TimeStamp @@ -297,14 +201,6 @@ nsresult nsNPAPIPluginInstance::Stop() } mRunning = DESTROYED; -#if MOZ_WIDGET_ANDROID - for (uint32_t i = 0; i < mPostedEvents.Length(); i++) { - mPostedEvents[i]->Cancel(); - } - - mPostedEvents.Clear(); -#endif - nsJSNPRuntime::OnPluginDestroy(&mNPP); if (error != NPERR_NO_ERROR) @@ -404,13 +300,8 @@ nsNPAPIPluginInstance::Start() mCachedParamValues[i] = ToNewUTF8String(attributes[i].mValue); } - // Android expects and empty string instead of null. mCachedParamNames[attributes.Length()] = ToNewUTF8String(NS_LITERAL_STRING("PARAM")); - #ifdef MOZ_WIDGET_ANDROID - mCachedParamValues[attributes.Length()] = ToNewUTF8String(NS_LITERAL_STRING("")); - #else - mCachedParamValues[attributes.Length()] = nullptr; - #endif + mCachedParamValues[attributes.Length()] = nullptr; for (uint32_t i = 0, pos = attributes.Length() + 1; i < params.Length(); i ++) { mCachedParamNames[pos] = ToNewUTF8String(params[i].mName); @@ -738,249 +629,6 @@ void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel) } #endif -#if defined(MOZ_WIDGET_ANDROID) - -static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, uint32_t aAction) -{ - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = aAction; - aInstance->HandleEvent(&event, nullptr); -} - -void nsNPAPIPluginInstance::NotifyForeground(bool aForeground) -{ - PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground)); - if (RUNNING != mRunning) - return; - - SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction); -} - -void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen) -{ - PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen)); - if (RUNNING != mRunning || mOnScreen == aOnScreen) - return; - - mOnScreen = aOnScreen; - SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction); -} - -void nsNPAPIPluginInstance::MemoryPressure() -{ - PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this)); - if (RUNNING != mRunning) - return; - - SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction); -} - -void nsNPAPIPluginInstance::NotifyFullScreen(bool aFullScreen) -{ - PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::NotifyFullScreen this=%p\n",this)); - - if (RUNNING != mRunning || mFullScreen == aFullScreen) - return; - - mFullScreen = aFullScreen; - SendLifecycleEvent(this, mFullScreen ? kEnterFullScreen_ANPLifecycleAction : kExitFullScreen_ANPLifecycleAction); - - if (mFullScreen && mFullScreenOrientation != dom::eScreenOrientation_None) { - java::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation); - } -} - -void nsNPAPIPluginInstance::NotifySize(nsIntSize size) -{ - if (kOpenGL_ANPDrawingModel != GetANPDrawingModel() || - size == mCurrentSize) - return; - - mCurrentSize = size; - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kDraw_ANPEventType; - event.data.draw.model = kOpenGL_ANPDrawingModel; - event.data.draw.data.surfaceSize.width = size.width; - event.data.draw.data.surfaceSize.height = size.height; - - HandleEvent(&event, nullptr); -} - -void nsNPAPIPluginInstance::SetANPDrawingModel(uint32_t aModel) -{ - mANPDrawingModel = aModel; -} - -void* nsNPAPIPluginInstance::GetJavaSurface() -{ - void* surface = nullptr; - nsresult rv = GetValueFromPlugin(kJavaSurface_ANPGetValue, &surface); - if (NS_FAILED(rv)) - return nullptr; - - return surface; -} - -void nsNPAPIPluginInstance::PostEvent(void* event) -{ - PluginEventRunnable *r = new PluginEventRunnable(this, (ANPEvent*)event); - mPostedEvents.AppendElement(RefPtr<PluginEventRunnable>(r)); - - NS_DispatchToMainThread(r); -} - -void nsNPAPIPluginInstance::SetFullScreenOrientation(uint32_t orientation) -{ - if (mFullScreenOrientation == orientation) - return; - - uint32_t oldOrientation = mFullScreenOrientation; - mFullScreenOrientation = orientation; - - if (mFullScreen) { - // We're already fullscreen so immediately apply the orientation change - - if (mFullScreenOrientation != dom::eScreenOrientation_None) { - java::GeckoAppShell::LockScreenOrientation(mFullScreenOrientation); - } else if (oldOrientation != dom::eScreenOrientation_None) { - // We applied an orientation when we entered fullscreen, but - // we don't want it anymore - java::GeckoAppShell::UnlockScreenOrientation(); - } - } -} - -void nsNPAPIPluginInstance::PopPostedEvent(PluginEventRunnable* r) -{ - mPostedEvents.RemoveElement(r); -} - -void nsNPAPIPluginInstance::SetWakeLock(bool aLocked) -{ - if (aLocked == mWakeLocked) - return; - - mWakeLocked = aLocked; - hal::ModifyWakeLock(NS_LITERAL_STRING("screen"), - mWakeLocked ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE, - hal::WAKE_LOCK_NO_CHANGE); -} - -GLContext* nsNPAPIPluginInstance::GLContext() -{ - if (!EnsureGLContext()) - return nullptr; - - return sPluginContext; -} - -already_AddRefed<AndroidSurfaceTexture> nsNPAPIPluginInstance::CreateSurfaceTexture() -{ - if (!EnsureGLContext()) - return nullptr; - - GLuint texture = TexturePoolOGL::AcquireTexture(); - if (!texture) - return nullptr; - - RefPtr<AndroidSurfaceTexture> surface = AndroidSurfaceTexture::Create(TexturePoolOGL::GetGLContext(), - texture); - if (!surface) { - return nullptr; - } - - nsCOMPtr<nsIRunnable> frameCallback = NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable); - surface->SetFrameAvailableCallback(frameCallback); - return surface.forget(); -} - -void nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable() -{ - if (mRunning == RUNNING && mOwner) - mOwner->Recomposite(); -} - -void* nsNPAPIPluginInstance::AcquireContentWindow() -{ - if (!mContentSurface) { - mContentSurface = CreateSurfaceTexture(); - - if (!mContentSurface) - return nullptr; - } - - return mContentSurface->NativeWindow(); -} - -AndroidSurfaceTexture* -nsNPAPIPluginInstance::AsSurfaceTexture() -{ - if (!mContentSurface) - return nullptr; - - return mContentSurface; -} - -void* nsNPAPIPluginInstance::AcquireVideoWindow() -{ - RefPtr<AndroidSurfaceTexture> surface = CreateSurfaceTexture(); - if (!surface) { - return nullptr; - } - - VideoInfo* info = new VideoInfo(surface); - - void* window = info->mSurfaceTexture->NativeWindow(); - mVideos.insert(std::pair<void*, VideoInfo*>(window, info)); - - return window; -} - -void nsNPAPIPluginInstance::ReleaseVideoWindow(void* window) -{ - std::map<void*, VideoInfo*>::iterator it = mVideos.find(window); - if (it == mVideos.end()) - return; - - delete it->second; - mVideos.erase(window); -} - -void nsNPAPIPluginInstance::SetVideoDimensions(void* window, gfxRect aDimensions) -{ - std::map<void*, VideoInfo*>::iterator it; - - it = mVideos.find(window); - if (it == mVideos.end()) - return; - - it->second->mDimensions = aDimensions; -} - -void nsNPAPIPluginInstance::GetVideos(nsTArray<VideoInfo*>& aVideos) -{ - std::map<void*, VideoInfo*>::iterator it; - for (it = mVideos.begin(); it != mVideos.end(); it++) - aVideos.AppendElement(it->second); -} - -nsNPAPIPluginInstance* nsNPAPIPluginInstance::GetFromNPP(NPP npp) -{ - std::map<NPP, nsNPAPIPluginInstance*>::iterator it; - - it = sPluginNPPMap.find(npp); - if (it == sPluginNPPMap.end()) - return nullptr; - - return it->second; -} - -#endif - nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel) { *aModel = (int32_t)mDrawingModel; @@ -1083,9 +731,8 @@ nsNPAPIPluginInstance::ShouldCache() nsresult nsNPAPIPluginInstance::IsWindowless(bool* isWindowless) { -#if defined(MOZ_WIDGET_ANDROID) || defined(XP_MACOSX) +#ifdef XP_MACOSX // All OS X plugins are windowless. - // On android, pre-honeycomb, all plugins are treated as windowless. *isWindowless = true; #else *isWindowless = mWindowless; diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index 5f0375637..48e62517d 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -18,15 +18,6 @@ #include <prinrval.h> #include "js/TypeDecls.h" #include "nsIAudioChannelAgent.h" -#ifdef MOZ_WIDGET_ANDROID -#include "nsIRunnable.h" -#include "GLContextTypes.h" -#include "AndroidSurfaceTexture.h" -#include "AndroidBridge.h" -#include <map> -class PluginEventRunnable; -#endif - #include "mozilla/EventForwards.h" #include "mozilla/TimeStamp.h" #include "mozilla/PluginLibrary.h" @@ -163,90 +154,6 @@ public: } #endif -#ifdef MOZ_WIDGET_ANDROID - void NotifyForeground(bool aForeground); - void NotifyOnScreen(bool aOnScreen); - void MemoryPressure(); - void NotifyFullScreen(bool aFullScreen); - void NotifySize(nsIntSize size); - - nsIntSize CurrentSize() { return mCurrentSize; } - - bool IsOnScreen() { - return mOnScreen; - } - - uint32_t GetANPDrawingModel() { return mANPDrawingModel; } - void SetANPDrawingModel(uint32_t aModel); - - void* GetJavaSurface(); - - void PostEvent(void* event); - - // These are really mozilla::dom::ScreenOrientation, but it's - // difficult to include that here - uint32_t FullScreenOrientation() { return mFullScreenOrientation; } - void SetFullScreenOrientation(uint32_t orientation); - - void SetWakeLock(bool aLock); - - mozilla::gl::GLContext* GLContext(); - - // For ANPOpenGL - class TextureInfo { - public: - TextureInfo() : - mTexture(0), mWidth(0), mHeight(0), mInternalFormat(0) - { - } - - TextureInfo(GLuint aTexture, int32_t aWidth, int32_t aHeight, GLuint aInternalFormat) : - mTexture(aTexture), mWidth(aWidth), mHeight(aHeight), mInternalFormat(aInternalFormat) - { - } - - GLuint mTexture; - int32_t mWidth; - int32_t mHeight; - GLuint mInternalFormat; - }; - - // For ANPNativeWindow - void* AcquireContentWindow(); - - mozilla::gl::AndroidSurfaceTexture* AsSurfaceTexture(); - - // For ANPVideo - class VideoInfo { - public: - VideoInfo(mozilla::gl::AndroidSurfaceTexture* aSurfaceTexture) : - mSurfaceTexture(aSurfaceTexture) - { - } - - ~VideoInfo() - { - mSurfaceTexture = nullptr; - } - - RefPtr<mozilla::gl::AndroidSurfaceTexture> mSurfaceTexture; - gfxRect mDimensions; - }; - - void* AcquireVideoWindow(); - void ReleaseVideoWindow(void* aWindow); - void SetVideoDimensions(void* aWindow, gfxRect aDimensions); - - void GetVideos(nsTArray<VideoInfo*>& aVideos); - - void SetOriginPos(mozilla::gl::OriginPos aOriginPos) { - mOriginPos = aOriginPos; - } - mozilla::gl::OriginPos OriginPos() const { return mOriginPos; } - - static nsNPAPIPluginInstance* GetFromNPP(NPP npp); -#endif - nsresult NewStreamListener(const char* aURL, void* notifyData, nsNPAPIPluginStreamListener** listener); @@ -347,23 +254,6 @@ protected: NPDrawingModel mDrawingModel; -#ifdef MOZ_WIDGET_ANDROID - uint32_t mANPDrawingModel; - - friend class PluginEventRunnable; - - nsTArray<RefPtr<PluginEventRunnable>> mPostedEvents; - void PopPostedEvent(PluginEventRunnable* r); - void OnSurfaceTextureFrameAvailable(); - - uint32_t mFullScreenOrientation; - bool mWakeLocked; - bool mFullScreen; - mozilla::gl::OriginPos mOriginPos; - - RefPtr<mozilla::gl::AndroidSurfaceTexture> mContentSurface; -#endif - enum { NOT_STARTED, RUNNING, @@ -410,15 +300,6 @@ private: // This is only valid when the plugin is actually stopped! mozilla::TimeStamp mStopTime; -#ifdef MOZ_WIDGET_ANDROID - already_AddRefed<mozilla::gl::AndroidSurfaceTexture> CreateSurfaceTexture(); - - std::map<void*, VideoInfo*> mVideos; - bool mOnScreen; - - nsIntSize mCurrentSize; -#endif - // is this instance Java and affected by bug 750480? bool mHaveJavaC2PJSObjectQuirk; @@ -434,13 +315,7 @@ private: bool mMuted; }; -// On Android, we need to guard against plugin code leaking entries in the local -// JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21 -#ifdef MOZ_WIDGET_ANDROID - #define MAIN_THREAD_JNI_REF_GUARD mozilla::AutoLocalJNIFrame jniFrame -#else - #define MAIN_THREAD_JNI_REF_GUARD -#endif +#define MAIN_THREAD_JNI_REF_GUARD void NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState); void NS_NotifyPluginCall(NSPluginCallReentry aReentryState); diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index c3de136d0..1b98855d1 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -104,11 +104,6 @@ #include "winbase.h" #endif -#ifdef ANDROID -#include <android/log.h> -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#endif - #include "npapi.h" using namespace mozilla; @@ -293,10 +288,6 @@ nsPluginHost::nsPluginHost() if (obsService) { obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); obsService->AddObserver(this, "blocklist-updated", false); -#ifdef MOZ_WIDGET_ANDROID - obsService->AddObserver(this, "application-foreground", false); - obsService->AddObserver(this, "application-background", false); -#endif } #ifdef PLUGIN_LOGGING @@ -2306,11 +2297,6 @@ WatchRegKey(uint32_t aRoot, nsCOMPtr<nsIWindowsRegKey>& aKey) nsresult nsPluginHost::LoadPlugins() { -#ifdef ANDROID - if (XRE_IsContentProcess()) { - return NS_OK; - } -#endif // do not do anything if it is already done // use ReloadPlugins() to enforce loading if (mPluginsLoaded) @@ -2453,10 +2439,6 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext); return NS_OK; } - } else { -#ifdef ANDROID - LOG("getting plugins dir failed"); -#endif } mPluginsLoaded = true; // at this point 'some' plugins have been loaded, @@ -3069,14 +3051,6 @@ nsPluginHost::ReadPluginInfo() if (!reader.NextLine()) return rv; -#if MOZ_WIDGET_ANDROID - // Flash on Android does not populate the version field, but it is tacked on to the description. - // For example, "Shockwave Flash 11.1 r115" - if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) { - version = &description[16]; - } -#endif - const char *name = reader.LinePtr(); if (!reader.NextLine()) return rv; @@ -3144,31 +3118,6 @@ nsPluginHost::ReadPluginInfo() mCachedPlugins = tag; } -// On Android we always want to try to load a plugin again (Flash). Bug 935676. -#ifndef MOZ_WIDGET_ANDROID - if (!ReadSectionHeader(reader, "INVALID")) { - return rv; - } - - while (reader.NextLine()) { - const char *fullpath = reader.LinePtr(); - if (!reader.NextLine()) { - return rv; - } - - const char *lastModifiedTimeStamp = reader.LinePtr(); - int64_t lastmod = nsCRT::atoll(lastModifiedTimeStamp); - - RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod); - - invalidTag->mNext = mInvalidPlugins; - if (mInvalidPlugins) { - mInvalidPlugins->mPrev = invalidTag; - } - mInvalidPlugins = invalidTag; - } -#endif - return NS_OK; } @@ -3491,24 +3440,7 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject, plugin = plugin->mNext; } } -#ifdef MOZ_WIDGET_ANDROID - if (!strcmp("application-background", aTopic)) { - for(uint32_t i = 0; i < mInstances.Length(); i++) { - mInstances[i]->NotifyForeground(false); - } - } - if (!strcmp("application-foreground", aTopic)) { - for(uint32_t i = 0; i < mInstances.Length(); i++) { - if (mInstances[i]->IsOnScreen()) - mInstances[i]->NotifyForeground(true); - } - } - if (!strcmp("memory-pressure", aTopic)) { - for(uint32_t i = 0; i < mInstances.Length(); i++) { - mInstances[i]->MemoryPressure(); - } - } -#endif + return NS_OK; } diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index d5b1eb9ea..22c4783ca 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -87,20 +87,6 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include <gtk/gtk.h> #endif -#ifdef MOZ_WIDGET_ANDROID -#include "ANPBase.h" -#include "AndroidBridge.h" -#include "ClientLayerManager.h" -#include "nsWindow.h" - -static nsPluginInstanceOwner* sFullScreenInstance = nullptr; - -using namespace mozilla::dom; - -#include <android/log.h> -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#endif - using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::layers; @@ -165,29 +151,6 @@ nsPluginInstanceOwner::NotifyPaintWaiter(nsDisplayListBuilder* aBuilder) } } -#if MOZ_WIDGET_ANDROID -static void -AttachToContainerAsSurfaceTexture(ImageContainer* container, - nsNPAPIPluginInstance* instance, - const LayoutDeviceRect& rect, - RefPtr<Image>* out_image) -{ - MOZ_ASSERT(out_image); - MOZ_ASSERT(!*out_image); - - mozilla::gl::AndroidSurfaceTexture* surfTex = instance->AsSurfaceTexture(); - if (!surfTex) { - return; - } - - RefPtr<Image> img = new SurfaceTextureImage( - surfTex, - gfx::IntSize::Truncate(rect.width, rect.height), - instance->OriginPos()); - *out_image = img; -} -#endif - bool nsPluginInstanceOwner::NeedsScrollImageLayer() { @@ -211,27 +174,6 @@ nsPluginInstanceOwner::GetImageContainer() RefPtr<ImageContainer> container; -#if MOZ_WIDGET_ANDROID - LayoutDeviceRect r = GetPluginRect(); - - // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render - // into, set y-flip flags, etc, so we do this at the beginning. - float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution(); - ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size(); - mInstance->NotifySize(nsIntSize::Truncate(screenSize.width, screenSize.height)); - - container = LayerManager::CreateImageContainer(); - - if (r.width && r.height) { - // Try to get it as an EGLImage first. - RefPtr<Image> img; - AttachToContainerAsSurfaceTexture(container, mInstance, r, &img); - - if (img) { - container->SetCurrentImageInTransaction(img); - } - } -#else if (NeedsScrollImageLayer()) { // windowed plugin under e10s #if defined(XP_WIN) @@ -241,7 +183,6 @@ nsPluginInstanceOwner::GetImageContainer() // async windowless rendering mInstance->GetImageContainer(getter_AddRefs(container)); } -#endif return container.forget(); } @@ -358,11 +299,6 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; -#ifdef MOZ_WIDGET_ANDROID - mFullScreen = false; - mJavaView = nullptr; -#endif - #ifdef XP_WIN mGotCompositionData = false; mSentStartComposition = false; @@ -387,10 +323,6 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() PLUG_DeletePluginNativeWindow(mPluginWindow); mPluginWindow = nullptr; -#ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); -#endif - if (mInstance) { mInstance->SetOwner(nullptr); } @@ -413,10 +345,6 @@ nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance *aInstance) // from our destructor. This fixes bug 613376. if (mInstance && !aInstance) { mInstance->SetOwner(nullptr); - -#ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); -#endif } mInstance = aInstance; @@ -601,7 +529,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect) if (!mPluginFrame || !invalidRect || !mWidgetVisible) return NS_ERROR_FAILURE; -#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) +#ifdef XP_MACOSX // Each time an asynchronously-drawing plugin sends a new surface to display, // the image in the ImageContainer is updated and InvalidateRect is called. // There are different side effects for (sync) Android plugins. @@ -624,6 +552,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect) } } #endif + nsIntRect rect(invalidRect->left, invalidRect->top, invalidRect->right - invalidRect->left, @@ -1485,185 +1414,6 @@ nsPluginInstanceOwner::GetEventloopNestingLevel() return currentLevel; } -#ifdef MOZ_WIDGET_ANDROID - -// Modified version of nsFrame::GetOffsetToCrossDoc that stops when it -// hits an element with a displayport (or runs out of frames). This is -// not really the right thing to do, but it's better than what was here before. -static nsPoint -GetOffsetRootContent(nsIFrame* aFrame) -{ - // offset will hold the final offset - // docOffset holds the currently accumulated offset at the current APD, it - // will be converted and added to offset when the current APD changes. - nsPoint offset(0, 0), docOffset(0, 0); - const nsIFrame* f = aFrame; - int32_t currAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); - int32_t apd = currAPD; - while (f) { - if (f->GetContent() && nsLayoutUtils::HasDisplayPort(f->GetContent())) - break; - - docOffset += f->GetPosition(); - nsIFrame* parent = f->GetParent(); - if (parent) { - f = parent; - } else { - nsPoint newOffset(0, 0); - f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset); - int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0; - if (!f || newAPD != currAPD) { - // Convert docOffset to the right APD and add it to offset. - offset += docOffset.ScaleToOtherAppUnits(currAPD, apd); - docOffset.x = docOffset.y = 0; - } - currAPD = newAPD; - docOffset += newOffset; - } - } - - offset += docOffset.ScaleToOtherAppUnits(currAPD, apd); - - return offset; -} - -LayoutDeviceRect nsPluginInstanceOwner::GetPluginRect() -{ - // Get the offset of the content relative to the page - nsRect bounds = mPluginFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mPluginFrame); - LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mPluginFrame->PresContext()->AppUnitsPerDevPixel()); - return LayoutDeviceRect(rect); -} - -bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = LayoutDeviceRect(0, 0, 0, 0) */) -{ - if (!mJavaView) { - mJavaView = mInstance->GetJavaSurface(); - - if (!mJavaView) - return false; - - mJavaView = (void*)jni::GetGeckoThreadEnv()->NewGlobalRef((jobject)mJavaView); - } - - if (mFullScreen) { - java::GeckoAppShell::AddFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView))); - sFullScreenInstance = this; - } - - return true; -} - -void nsPluginInstanceOwner::RemovePluginView() -{ - if (!mInstance || !mJavaView) - return; - - if (mFullScreen) { - java::GeckoAppShell::RemoveFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView))); - } - jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView); - mJavaView = nullptr; - - if (mFullScreen) - sFullScreenInstance = nullptr; -} - -void -nsPluginInstanceOwner::GetVideos(nsTArray<nsNPAPIPluginInstance::VideoInfo*>& aVideos) -{ - if (!mInstance) - return; - - mInstance->GetVideos(aVideos); -} - -already_AddRefed<ImageContainer> -nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo) -{ - RefPtr<ImageContainer> container = LayerManager::CreateImageContainer(); - - if (aVideoInfo->mDimensions.width && aVideoInfo->mDimensions.height) { - RefPtr<Image> img = new SurfaceTextureImage( - aVideoInfo->mSurfaceTexture, - gfx::IntSize::Truncate(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height), - gl::OriginPos::BottomLeft); - container->SetCurrentImageInTransaction(img); - } - - return container.forget(); -} - -void nsPluginInstanceOwner::Invalidate() { - NPRect rect; - rect.left = rect.top = 0; - rect.right = mPluginWindow->width; - rect.bottom = mPluginWindow->height; - InvalidateRect(&rect); -} - -void nsPluginInstanceOwner::Recomposite() { - nsIWidget* const widget = mPluginFrame->GetNearestWidget(); - NS_ENSURE_TRUE_VOID(widget); - - LayerManager* const lm = widget->GetLayerManager(); - NS_ENSURE_TRUE_VOID(lm); - - ClientLayerManager* const clm = lm->AsClientLayerManager(); - NS_ENSURE_TRUE_VOID(clm && clm->GetRoot()); - - clm->SendInvalidRegion( - clm->GetRoot()->GetLocalVisibleRegion().ToUnknownRegion().GetBounds()); - clm->Composite(); -} - -void nsPluginInstanceOwner::RequestFullScreen() { - if (mFullScreen) - return; - - // Remove whatever view we currently have (if any, fullscreen or otherwise) - RemovePluginView(); - - mFullScreen = true; - AddPluginView(); - - mInstance->NotifyFullScreen(mFullScreen); -} - -void nsPluginInstanceOwner::ExitFullScreen() { - if (!mFullScreen) - return; - - RemovePluginView(); - - mFullScreen = false; - - int32_t model = mInstance->GetANPDrawingModel(); - - if (model == kSurface_ANPDrawingModel) { - // We need to do this immediately, otherwise Flash - // sometimes causes a deadlock (bug 762407) - AddPluginView(GetPluginRect()); - } - - mInstance->NotifyFullScreen(mFullScreen); - - // This will cause Paint() to be called, which is where - // we normally add/update views and layers - Invalidate(); -} - -void nsPluginInstanceOwner::ExitFullScreen(jobject view) { - JNIEnv* env = jni::GetGeckoThreadEnv(); - - if (sFullScreenInstance && sFullScreenInstance->mInstance && - env->IsSameObject(view, (jobject)sFullScreenInstance->mInstance->GetJavaSurface())) { - sFullScreenInstance->ExitFullScreen(); - } -} - -#endif - void nsPluginInstanceOwner::NotifyHostAsyncInitFailed() { @@ -1705,27 +1455,6 @@ nsPluginInstanceOwner::NotifyDestroyPending() nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) { -#ifdef MOZ_WIDGET_ANDROID - if (mInstance) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - - nsAutoString eventType; - aFocusEvent->GetType(eventType); - if (eventType.EqualsLiteral("focus")) { - event.data.lifecycle.action = kGainFocus_ANPLifecycleAction; - } - else if (eventType.EqualsLiteral("blur")) { - event.data.lifecycle.action = kLoseFocus_ANPLifecycleAction; - } - else { - NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType"); - } - mInstance->HandleEvent(&event, nullptr); - } -#endif - #ifndef XP_MACOSX if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) { // continue only for cases without child window @@ -2786,96 +2515,6 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) rv = nsEventStatus_eConsumeNoDefault; #endif -#ifdef MOZ_WIDGET_ANDROID - // this code supports windowless plugins - { - // The plugin needs focus to receive keyboard and touch events - nsIFocusManager* fm = nsFocusManager::GetFocusManager(); - if (fm) { - nsCOMPtr<nsIDOMElement> elem = do_QueryReferent(mContent); - fm->SetFocus(elem, 0); - } - } - switch(anEvent.mClass) { - case eMouseEventClass: - { - switch (anEvent.mMessage) { - case eMouseClick: - case eMouseDoubleClick: - case eMouseAuxClick: - // Button up/down events sent instead. - return rv; - default: - break; - } - - // Get reference point relative to plugin origin. - const nsPresContext* presContext = mPluginFrame->PresContext(); - nsPoint appPoint = - nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) - - mPluginFrame->GetContentRectRelativeToSelf().TopLeft(); - nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x), - presContext->AppUnitsToDevPixels(appPoint.y)); - - switch (anEvent.mMessage) { - case eMouseMove: - { - // are these going to be touch events? - // pluginPoint.x; - // pluginPoint.y; - } - break; - case eMouseDown: - { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kMouse_ANPEventType; - event.data.mouse.action = kDown_ANPMouseAction; - event.data.mouse.x = pluginPoint.x; - event.data.mouse.y = pluginPoint.y; - mInstance->HandleEvent(&event, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); - } - break; - case eMouseUp: - { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kMouse_ANPEventType; - event.data.mouse.action = kUp_ANPMouseAction; - event.data.mouse.x = pluginPoint.x; - event.data.mouse.y = pluginPoint.y; - mInstance->HandleEvent(&event, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); - } - break; - default: - break; - } - } - break; - - case eKeyboardEventClass: - { - const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent(); - LOG("Firing eKeyboardEventClass %d %d\n", - keyEvent.mKeyCode, keyEvent.mCharCode); - // pluginEvent is initialized by nsWindow::InitKeyEvent(). - const ANPEvent* pluginEvent = static_cast<const ANPEvent*>(keyEvent.mPluginEvent); - if (pluginEvent) { - MOZ_ASSERT(pluginEvent->inSize == sizeof(ANPEvent)); - MOZ_ASSERT(pluginEvent->eventType == kKey_ANPEventType); - mInstance->HandleEvent(const_cast<ANPEvent*>(pluginEvent), - nullptr, - NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); - } - } - break; - - default: - break; - } - rv = nsEventStatus_eConsumeNoDefault; -#endif - return rv; } @@ -2922,10 +2561,6 @@ nsPluginInstanceOwner::Destroy() this, true); content->RemoveSystemEventListener(NS_LITERAL_STRING("text"), this, true); -#if MOZ_WIDGET_ANDROID - RemovePluginView(); -#endif - if (mWidget) { if (mPluginWindow) { mPluginWindow->SetPluginWidget(nullptr); @@ -2995,72 +2630,6 @@ void nsPluginInstanceOwner::Paint(const RECT& aDirty, HDC aDC) } #endif -#ifdef MOZ_WIDGET_ANDROID - -void nsPluginInstanceOwner::Paint(gfxContext* aContext, - const gfxRect& aFrameRect, - const gfxRect& aDirtyRect) -{ - if (!mInstance || !mPluginFrame || !mPluginDocumentActiveState || mFullScreen) - return; - - int32_t model = mInstance->GetANPDrawingModel(); - - if (model == kSurface_ANPDrawingModel) { - if (!AddPluginView(GetPluginRect())) { - Invalidate(); - } - return; - } - - if (model != kBitmap_ANPDrawingModel) - return; - -#ifdef ANP_BITMAP_DRAWING_MODEL - static RefPtr<gfxImageSurface> pluginSurface; - - if (pluginSurface == nullptr || - aFrameRect.width != pluginSurface->Width() || - aFrameRect.height != pluginSurface->Height()) { - - pluginSurface = new gfxImageSurface(gfx::IntSize(aFrameRect.width, aFrameRect.height), - SurfaceFormat::A8R8G8B8_UINT32); - if (!pluginSurface) - return; - } - - // Clears buffer. I think this is needed. - gfxUtils::ClearThebesSurface(pluginSurface); - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = 4; - event.data.draw.model = 1; - - event.data.draw.clip.top = 0; - event.data.draw.clip.left = 0; - event.data.draw.clip.bottom = aFrameRect.width; - event.data.draw.clip.right = aFrameRect.height; - - event.data.draw.data.bitmap.format = kRGBA_8888_ANPBitmapFormat; - event.data.draw.data.bitmap.width = aFrameRect.width; - event.data.draw.data.bitmap.height = aFrameRect.height; - event.data.draw.data.bitmap.baseAddr = pluginSurface->Data(); - event.data.draw.data.bitmap.rowBytes = aFrameRect.width * 4; - - if (!mInstance) - return; - - mInstance->HandleEvent(&event, nullptr); - - aContext->SetOp(gfx::CompositionOp::OP_SOURCE); - aContext->SetSource(pluginSurface, gfxPoint(aFrameRect.x, aFrameRect.y)); - aContext->Clip(aFrameRect); - aContext->Paint(); -#endif -} -#endif - #if defined(MOZ_X11) void nsPluginInstanceOwner::Paint(gfxContext* aContext, const gfxRect& aFrameRect, @@ -3663,24 +3232,6 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) #ifndef XP_MACOSX UpdateWindowPositionAndClipRect(true); -#ifdef MOZ_WIDGET_ANDROID - if (mInstance) { - if (!mPluginDocumentActiveState) { - RemovePluginView(); - } - - mInstance->NotifyOnScreen(mPluginDocumentActiveState); - - // This is, perhaps, incorrect. It is supposed to be sent - // when "the webview has paused or resumed". The side effect - // is that Flash video players pause or resume (if they were - // playing before) based on the value here. I personally think - // we want that on Android when switching to another tab, so - // that's why we call it here. - mInstance->NotifyForeground(mPluginDocumentActiveState); - } -#endif // #ifdef MOZ_WIDGET_ANDROID - // We don't have a connection to PluginWidgetParent in the chrome // process when dealing with tab visibility changes, so this needs // to be forwarded over after the active state is updated. If we diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index 589bcb02c..2fa67c86e 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -29,7 +29,7 @@ class nsPluginDOMContextMenuListener; class nsPluginFrame; class nsDisplayListBuilder; -#if defined(MOZ_X11) || defined(ANDROID) +#ifdef MOZ_X11 class gfxContext; #endif @@ -108,7 +108,7 @@ public: void Paint(const gfxRect& aDirtyRect, CGContextRef cgContext); void RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight); void DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext); -#elif defined(MOZ_X11) || defined(ANDROID) +#elif defined(MOZ_X11) void Paint(gfxContext* aContext, const gfxRect& aFrameRect, const gfxRect& aDirtyRect); @@ -254,21 +254,6 @@ public: already_AddRefed<nsIURI> GetBaseURI() const; -#ifdef MOZ_WIDGET_ANDROID - // Returns the image container for the specified VideoInfo - void GetVideos(nsTArray<nsNPAPIPluginInstance::VideoInfo*>& aVideos); - already_AddRefed<mozilla::layers::ImageContainer> GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo); - - void Invalidate(); - void Recomposite(); - - void RequestFullScreen(); - void ExitFullScreen(); - - // Called from nsAppShell when we removed the fullscreen view. - static void ExitFullScreen(jobject view); -#endif - void NotifyHostAsyncInitFailed(); void NotifyHostCreateWidget(); void NotifyDestroyPending(); @@ -306,16 +291,7 @@ private: size == nsIntSize(mPluginWindow->width, mPluginWindow->height); } -#ifdef MOZ_WIDGET_ANDROID - mozilla::LayoutDeviceRect GetPluginRect(); - bool AddPluginView(const mozilla::LayoutDeviceRect& aRect = mozilla::LayoutDeviceRect(0, 0, 0, 0)); - void RemovePluginView(); - - bool mFullScreen; - void* mJavaView; -#endif - -#if defined(XP_WIN) +#ifdef XP_WIN nsIWidget* GetContainingWidgetIfOffset(); already_AddRefed<mozilla::TextComposition> GetTextComposition(); void HandleNoConsumedCompositionMessage( diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index a794c416c..58b413388 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -735,10 +735,6 @@ nsPluginTag::GetNiceName(nsACString & aResult) NS_IMETHODIMP nsPluginTag::GetBlocklistState(uint32_t *aResult) { -#if defined(MOZ_WIDGET_ANDROID) - *aResult = nsIBlocklistService::STATE_NOT_BLOCKED; - return NS_OK; -#else if (mCachedBlocklistStateValid) { *aResult = mCachedBlocklistState; return NS_OK; @@ -772,7 +768,6 @@ nsPluginTag::GetBlocklistState(uint32_t *aResult) mCachedBlocklistState = (uint16_t) *aResult; mCachedBlocklistStateValid = true; return NS_OK; -#endif // defined(MOZ_WIDGET_ANDROID) } void diff --git a/dom/plugins/base/nsPluginsDirUnix.cpp b/dom/plugins/base/nsPluginsDirUnix.cpp index de3b7a2d1..55a4eb0d6 100644 --- a/dom/plugins/base/nsPluginsDirUnix.cpp +++ b/dom/plugins/base/nsPluginsDirUnix.cpp @@ -206,16 +206,6 @@ bool nsPluginsDir::IsPluginFile(nsIFile* file) if (NS_FAILED(file->GetNativeLeafName(filename))) return false; -#ifdef ANDROID - // It appears that if you load - // 'libstagefright_honeycomb.so' on froyo, or - // 'libstagefright_froyo.so' on honeycomb, we will abort. - // Since these are just helper libs, we can ignore. - const char *cFile = filename.get(); - if (strstr(cFile, "libstagefright") != nullptr) - return false; -#endif - NS_NAMED_LITERAL_CSTRING(dllSuffix, LOCAL_PLUGIN_DLL_SUFFIX); if (filename.Length() > dllSuffix.Length() && StringEndsWith(filename, dllSuffix)) diff --git a/dom/plugins/ipc/NPEventAndroid.h b/dom/plugins/ipc/NPEventAndroid.h deleted file mode 100644 index f664af857..000000000 --- a/dom/plugins/ipc/NPEventAndroid.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ -/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ -/* 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/. */ - -// This is a NPEventX11.h derived stub for Android -// Plugins aren't actually supported yet - -#ifndef mozilla_dom_plugins_NPEventAndroid_h -#define mozilla_dom_plugins_NPEventAndroid_h - -#include "npapi.h" - -namespace mozilla { - -namespace plugins { - -struct NPRemoteEvent { - NPEvent event; -}; - -} - -} - - -namespace IPC { - -template <> -struct ParamTraits<mozilla::plugins::NPRemoteEvent> -{ - typedef mozilla::plugins::NPRemoteEvent paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - aMsg->WriteBytes(&aParam, sizeof(paramType)); - } - - static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) - { - return aMsg->ReadBytesInto(aIter, aResult, sizeof(paramType)); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - // TODO - aLog->append(L"(AndroidEvent)"); - } -}; - -} // namespace IPC - - -#endif // mozilla_dom_plugins_NPEventAndroid_h diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 3f2cdbc13..a4f6b6b51 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -1427,8 +1427,6 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) if (mPluginIface->setwindow) (void) mPluginIface->setwindow(&mData, &mWindow); -#elif defined(ANDROID) - // TODO: Need Android impl #elif defined(MOZ_WIDGET_UIKIT) // Don't care #else diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index bdd15ca99..24b1410bc 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -265,9 +265,6 @@ PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* XID id; #elif defined(XP_DARWIN) intptr_t id; -#elif defined(ANDROID) - // TODO: Need Android impl - int id; #else #warning Implement me #endif diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h index 4532fac93..a9cd52ae2 100644 --- a/dom/plugins/ipc/PluginMessageUtils.h +++ b/dom/plugins/ipc/PluginMessageUtils.h @@ -111,7 +111,7 @@ struct NPAudioDeviceChangeDetailsIPC typedef HWND NativeWindowHandle; #elif defined(MOZ_X11) typedef XID NativeWindowHandle; -#elif defined(XP_DARWIN) || defined(ANDROID) +#elif defined(XP_DARWIN) typedef intptr_t NativeWindowHandle; // never actually used, will always be 0 #else #error Need NativeWindowHandle for this platform @@ -736,8 +736,6 @@ struct ParamTraits<mozilla::plugins::NPAudioDeviceChangeDetailsIPC> # include "mozilla/plugins/NPEventOSX.h" #elif defined(XP_WIN) # include "mozilla/plugins/NPEventWindows.h" -#elif defined(ANDROID) -# include "mozilla/plugins/NPEventAndroid.h" #elif defined(XP_UNIX) # include "mozilla/plugins/NPEventUnix.h" #else diff --git a/dom/plugins/ipc/moz.build b/dom/plugins/ipc/moz.build index 15ed6410d..051d5a3b4 100644 --- a/dom/plugins/ipc/moz.build +++ b/dom/plugins/ipc/moz.build @@ -17,7 +17,6 @@ EXPORTS.mozilla.plugins += [ 'BrowserStreamParent.h', 'ChildAsyncCall.h', 'ChildTimer.h', - 'NPEventAndroid.h', 'NPEventOSX.h', 'NPEventUnix.h', 'NPEventWindows.h', diff --git a/dom/presentation/AvailabilityCollection.cpp b/dom/presentation/AvailabilityCollection.cpp deleted file mode 100644 index 73752c750..000000000 --- a/dom/presentation/AvailabilityCollection.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "AvailabilityCollection.h" - -#include "mozilla/ClearOnShutdown.h" -#include "PresentationAvailability.h" - -namespace mozilla { -namespace dom { - -/* static */ -StaticAutoPtr<AvailabilityCollection> -AvailabilityCollection::sSingleton; -static bool gOnceAliveNowDead = false; - -/* static */ AvailabilityCollection* -AvailabilityCollection::GetSingleton() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!sSingleton && !gOnceAliveNowDead) { - sSingleton = new AvailabilityCollection(); - ClearOnShutdown(&sSingleton); - } - - return sSingleton; -} - -AvailabilityCollection::AvailabilityCollection() -{ - MOZ_COUNT_CTOR(AvailabilityCollection); -} - -AvailabilityCollection::~AvailabilityCollection() -{ - MOZ_COUNT_DTOR(AvailabilityCollection); - gOnceAliveNowDead = true; -} - -void -AvailabilityCollection::Add(PresentationAvailability* aAvailability) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!aAvailability) { - return; - } - - WeakPtr<PresentationAvailability> availability = aAvailability; - if (mAvailabilities.Contains(aAvailability)) { - return; - } - - mAvailabilities.AppendElement(aAvailability); -} - -void -AvailabilityCollection::Remove(PresentationAvailability* aAvailability) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!aAvailability) { - return; - } - - WeakPtr<PresentationAvailability> availability = aAvailability; - mAvailabilities.RemoveElement(availability); -} - -already_AddRefed<PresentationAvailability> -AvailabilityCollection::Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // Loop backwards to allow removing elements in the loop. - for (int i = mAvailabilities.Length() - 1; i >= 0; --i) { - WeakPtr<PresentationAvailability> availability = mAvailabilities[i]; - if (!availability) { - // The availability object was destroyed. Remove it from the list. - mAvailabilities.RemoveElementAt(i); - continue; - } - - if (availability->Equals(aWindowId, aUrls)) { - RefPtr<PresentationAvailability> matchedAvailability = availability.get(); - return matchedAvailability.forget(); - } - } - - - return nullptr; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/AvailabilityCollection.h b/dom/presentation/AvailabilityCollection.h deleted file mode 100644 index d2faae4c2..000000000 --- a/dom/presentation/AvailabilityCollection.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_AvailabilityCollection_h -#define mozilla_dom_AvailabilityCollection_h - -#include "mozilla/StaticPtr.h" -#include "mozilla/WeakPtr.h" -#include "nsString.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { - -class PresentationAvailability; - -class AvailabilityCollection final -{ -public: - static AvailabilityCollection* GetSingleton(); - - void Add(PresentationAvailability* aAvailability); - - void Remove(PresentationAvailability* aAvailability); - - already_AddRefed<PresentationAvailability> - Find(const uint64_t aWindowId, const nsTArray<nsString>& aUrls); - -private: - friend class StaticAutoPtr<AvailabilityCollection>; - - AvailabilityCollection(); - virtual ~AvailabilityCollection(); - - static StaticAutoPtr<AvailabilityCollection> sSingleton; - nsTArray<WeakPtr<PresentationAvailability>> mAvailabilities; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_AvailabilityCollection_h diff --git a/dom/presentation/ControllerConnectionCollection.cpp b/dom/presentation/ControllerConnectionCollection.cpp deleted file mode 100644 index 7d3ffe684..000000000 --- a/dom/presentation/ControllerConnectionCollection.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "ControllerConnectionCollection.h" - -#include "mozilla/ClearOnShutdown.h" -#include "nsIPresentationService.h" -#include "PresentationConnection.h" - -namespace mozilla { -namespace dom { - -/* static */ -StaticAutoPtr<ControllerConnectionCollection> -ControllerConnectionCollection::sSingleton; - -/* static */ ControllerConnectionCollection* -ControllerConnectionCollection::GetSingleton() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!sSingleton) { - sSingleton = new ControllerConnectionCollection(); - ClearOnShutdown(&sSingleton); - } - - return sSingleton; -} - -ControllerConnectionCollection::ControllerConnectionCollection() -{ - MOZ_COUNT_CTOR(ControllerConnectionCollection); -} - -ControllerConnectionCollection::~ControllerConnectionCollection() -{ - MOZ_COUNT_DTOR(ControllerConnectionCollection); -} - -void -ControllerConnectionCollection::AddConnection( - PresentationConnection* aConnection, - const uint8_t aRole) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "This is allowed only to be called at controller side."); - return; - } - - if (!aConnection) { - return; - } - - WeakPtr<PresentationConnection> connection = aConnection; - if (mConnections.Contains(connection)) { - return; - } - - mConnections.AppendElement(connection); -} - -void -ControllerConnectionCollection::RemoveConnection( - PresentationConnection* aConnection, - const uint8_t aRole) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "This is allowed only to be called at controller side."); - return; - } - - if (!aConnection) { - return; - } - - WeakPtr<PresentationConnection> connection = aConnection; - mConnections.RemoveElement(connection); -} - -already_AddRefed<PresentationConnection> -ControllerConnectionCollection::FindConnection( - uint64_t aWindowId, - const nsAString& aId, - const uint8_t aRole) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "This is allowed only to be called at controller side."); - return nullptr; - } - - // Loop backwards to allow removing elements in the loop. - for (int i = mConnections.Length() - 1; i >= 0; --i) { - WeakPtr<PresentationConnection> connection = mConnections[i]; - if (!connection) { - // The connection was destroyed. Remove it from the list. - mConnections.RemoveElementAt(i); - continue; - } - - if (connection->Equals(aWindowId, aId)) { - RefPtr<PresentationConnection> matchedConnection = connection.get(); - return matchedConnection.forget(); - } - } - - return nullptr; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ControllerConnectionCollection.h b/dom/presentation/ControllerConnectionCollection.h deleted file mode 100644 index c5300fe30..000000000 --- a/dom/presentation/ControllerConnectionCollection.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_ControllerConnectionCollection_h -#define mozilla_dom_ControllerConnectionCollection_h - -#include "mozilla/StaticPtr.h" -#include "mozilla/WeakPtr.h" -#include "nsString.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { - -class PresentationConnection; - -class ControllerConnectionCollection final -{ -public: - static ControllerConnectionCollection* GetSingleton(); - - void AddConnection(PresentationConnection* aConnection, - const uint8_t aRole); - - void RemoveConnection(PresentationConnection* aConnection, - const uint8_t aRole); - - already_AddRefed<PresentationConnection> - FindConnection(uint64_t aWindowId, - const nsAString& aId, - const uint8_t aRole); - -private: - friend class StaticAutoPtr<ControllerConnectionCollection>; - - ControllerConnectionCollection(); - virtual ~ControllerConnectionCollection(); - - static StaticAutoPtr<ControllerConnectionCollection> sSingleton; - nsTArray<WeakPtr<PresentationConnection>> mConnections; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_ControllerConnectionCollection_h diff --git a/dom/presentation/DCPresentationChannelDescription.cpp b/dom/presentation/DCPresentationChannelDescription.cpp deleted file mode 100644 index a904dfe3f..000000000 --- a/dom/presentation/DCPresentationChannelDescription.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "DCPresentationChannelDescription.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(DCPresentationChannelDescription, - nsIPresentationChannelDescription) - -NS_IMETHODIMP -DCPresentationChannelDescription::GetType(uint8_t* aRetVal) -{ - if (NS_WARN_IF(!aRetVal)) { - return NS_ERROR_INVALID_POINTER; - } - - *aRetVal = nsIPresentationChannelDescription::TYPE_DATACHANNEL; - return NS_OK; -} - -NS_IMETHODIMP -DCPresentationChannelDescription::GetTcpAddress(nsIArray** aRetVal) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -DCPresentationChannelDescription::GetTcpPort(uint16_t* aRetVal) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -DCPresentationChannelDescription::GetDataChannelSDP(nsAString& aDataChannelSDP) -{ - aDataChannelSDP = mSDP; - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/DCPresentationChannelDescription.h b/dom/presentation/DCPresentationChannelDescription.h deleted file mode 100644 index 63a058f9a..000000000 --- a/dom/presentation/DCPresentationChannelDescription.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_DCPresentationChannelDescription_h -#define mozilla_dom_DCPresentationChannelDescription_h - -#include "nsIPresentationControlChannel.h" -#include "nsString.h" - -namespace mozilla { -namespace dom { - -// PresentationChannelDescription for Data Channel -class DCPresentationChannelDescription final : public nsIPresentationChannelDescription -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONCHANNELDESCRIPTION - - explicit DCPresentationChannelDescription(const nsAString& aSDP) - : mSDP(aSDP) - { - } - -private: - virtual ~DCPresentationChannelDescription() = default; - - nsString mSDP; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_DCPresentationChannelDescription_h diff --git a/dom/presentation/Presentation.cpp b/dom/presentation/Presentation.cpp deleted file mode 100644 index 07ca12f26..000000000 --- a/dom/presentation/Presentation.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "Presentation.h" - -#include <ctype.h> - -#include "mozilla/dom/PresentationBinding.h" -#include "mozilla/dom/Promise.h" -#include "nsContentUtils.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIDocShell.h" -#include "nsIPresentationService.h" -#include "nsIScriptSecurityManager.h" -#include "nsJSUtils.h" -#include "nsNetUtil.h" -#include "nsPIDOMWindow.h" -#include "nsSandboxFlags.h" -#include "nsServiceManagerUtils.h" -#include "PresentationReceiver.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Presentation, - mWindow, - mDefaultRequest, mReceiver) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(Presentation) -NS_IMPL_CYCLE_COLLECTING_RELEASE(Presentation) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Presentation) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -/* static */ already_AddRefed<Presentation> -Presentation::Create(nsPIDOMWindowInner* aWindow) -{ - RefPtr<Presentation> presentation = new Presentation(aWindow); - return presentation.forget(); -} - -Presentation::Presentation(nsPIDOMWindowInner* aWindow) - : mWindow(aWindow) -{ -} - -Presentation::~Presentation() -{ -} - -/* virtual */ JSObject* -Presentation::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationBinding::Wrap(aCx, this, aGivenProto); -} - -void -Presentation::SetDefaultRequest(PresentationRequest* aRequest) -{ - nsCOMPtr<nsIDocument> doc = mWindow ? mWindow->GetExtantDoc() : nullptr; - if (NS_WARN_IF(!doc)) { - return; - } - - if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) { - return; - } - - mDefaultRequest = aRequest; -} - -already_AddRefed<PresentationRequest> -Presentation::GetDefaultRequest() const -{ - RefPtr<PresentationRequest> request = mDefaultRequest; - return request.forget(); -} - -already_AddRefed<PresentationReceiver> -Presentation::GetReceiver() -{ - // return the same receiver if already created - if (mReceiver) { - RefPtr<PresentationReceiver> receiver = mReceiver; - return receiver.forget(); - } - - if (!HasReceiverSupport() || !IsInPresentedContent()) { - return nullptr; - } - - mReceiver = PresentationReceiver::Create(mWindow); - if (NS_WARN_IF(!mReceiver)) { - MOZ_ASSERT(mReceiver); - return nullptr; - } - - RefPtr<PresentationReceiver> receiver = mReceiver; - return receiver.forget(); -} - -void -Presentation::SetStartSessionUnsettled(bool aIsUnsettled) -{ - mStartSessionUnsettled = aIsUnsettled; -} - -bool -Presentation::IsStartSessionUnsettled() const -{ - return mStartSessionUnsettled; -} - -bool -Presentation::HasReceiverSupport() const -{ - if (!mWindow) { - return false; - } - - // Grant access to browser receiving pages and their same-origin iframes. (App - // pages should be controlled by "presentation" permission in app manifests.) - nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell(); - if (!docShell) { - return false; - } - - if (!Preferences::GetBool("dom.presentation.testing.simulate-receiver") && - !docShell->GetIsInMozBrowserOrApp() && - !docShell->GetIsTopLevelContentDocShell()) { - return false; - } - - nsAutoString presentationURL; - nsContentUtils::GetPresentationURL(docShell, presentationURL); - - if (presentationURL.IsEmpty()) { - return false; - } - - nsCOMPtr<nsIScriptSecurityManager> securityManager = - nsContentUtils::GetSecurityManager(); - if (!securityManager) { - return false; - } - - nsCOMPtr<nsIURI> presentationURI; - nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL); - if (NS_FAILED(rv)) { - return false; - } - - nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI(); - return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI, - docURI, - false)); -} - -bool -Presentation::IsInPresentedContent() const -{ - if (!mWindow) { - return false; - } - - nsCOMPtr<nsIDocShell> docShell = mWindow->GetDocShell(); - MOZ_ASSERT(docShell); - - nsAutoString presentationURL; - nsContentUtils::GetPresentationURL(docShell, presentationURL); - - return !presentationURL.IsEmpty(); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/Presentation.h b/dom/presentation/Presentation.h deleted file mode 100644 index 08d0003b3..000000000 --- a/dom/presentation/Presentation.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_Presentation_h -#define mozilla_dom_Presentation_h - -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" -#include "nsISupportsImpl.h" -#include "nsWrapperCache.h" - -class nsPIDOMWindowInner; - -namespace mozilla { -namespace dom { - -class Promise; -class PresentationReceiver; -class PresentationRequest; - -class Presentation final : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Presentation) - - static already_AddRefed<Presentation> Create(nsPIDOMWindowInner* aWindow); - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - nsPIDOMWindowInner* GetParentObject() const - { - return mWindow; - } - - // WebIDL (public APIs) - void SetDefaultRequest(PresentationRequest* aRequest); - - already_AddRefed<PresentationRequest> GetDefaultRequest() const; - - already_AddRefed<PresentationReceiver> GetReceiver(); - - // For bookkeeping unsettled start session request - void SetStartSessionUnsettled(bool aIsUnsettled); - bool IsStartSessionUnsettled() const; - -private: - explicit Presentation(nsPIDOMWindowInner* aWindow); - - virtual ~Presentation(); - - bool HasReceiverSupport() const; - - bool IsInPresentedContent() const; - - RefPtr<PresentationRequest> mDefaultRequest; - RefPtr<PresentationReceiver> mReceiver; - nsCOMPtr<nsPIDOMWindowInner> mWindow; - bool mStartSessionUnsettled = false; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_Presentation_h diff --git a/dom/presentation/PresentationAvailability.cpp b/dom/presentation/PresentationAvailability.cpp deleted file mode 100644 index 93f27dfbf..000000000 --- a/dom/presentation/PresentationAvailability.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "PresentationAvailability.h" - -#include "mozilla/dom/PresentationAvailabilityBinding.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/Unused.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIPresentationDeviceManager.h" -#include "nsIPresentationService.h" -#include "nsServiceManagerUtils.h" -#include "PresentationLog.h" - -using namespace mozilla; -using namespace mozilla::dom; - -NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationAvailability) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationAvailability, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromises); - tmp->Shutdown(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_ADDREF_INHERITED(PresentationAvailability, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(PresentationAvailability, DOMEventTargetHelper) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationAvailability) - NS_INTERFACE_MAP_ENTRY(nsIPresentationAvailabilityListener) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -/* static */ already_AddRefed<PresentationAvailability> -PresentationAvailability::Create(nsPIDOMWindowInner* aWindow, - const nsTArray<nsString>& aUrls, - RefPtr<Promise>& aPromise) -{ - RefPtr<PresentationAvailability> availability = - new PresentationAvailability(aWindow, aUrls); - return NS_WARN_IF(!availability->Init(aPromise)) ? nullptr - : availability.forget(); -} - -PresentationAvailability::PresentationAvailability(nsPIDOMWindowInner* aWindow, - const nsTArray<nsString>& aUrls) - : DOMEventTargetHelper(aWindow) - , mIsAvailable(false) - , mUrls(aUrls) -{ - for (uint32_t i = 0; i < mUrls.Length(); ++i) { - mAvailabilityOfUrl.AppendElement(false); - } -} - -PresentationAvailability::~PresentationAvailability() -{ - Shutdown(); -} - -bool -PresentationAvailability::Init(RefPtr<Promise>& aPromise) -{ - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return false; - } - - nsresult rv = service->RegisterAvailabilityListener(mUrls, this); - if (NS_WARN_IF(NS_FAILED(rv))) { - // If the user agent is unable to monitor available device, - // Resolve promise with |value| set to false. - mIsAvailable = false; - aPromise->MaybeResolve(this); - return true; - } - - EnqueuePromise(aPromise); - - AvailabilityCollection* collection = AvailabilityCollection::GetSingleton(); - if (collection) { - collection->Add(this); - } - - return true; -} - -void PresentationAvailability::Shutdown() -{ - AvailabilityCollection* collection = AvailabilityCollection::GetSingleton(); - if (collection ) { - collection->Remove(this); - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return; - } - - Unused << - NS_WARN_IF(NS_FAILED(service->UnregisterAvailabilityListener(mUrls, - this))); -} - -/* virtual */ void -PresentationAvailability::DisconnectFromOwner() -{ - Shutdown(); - DOMEventTargetHelper::DisconnectFromOwner(); -} - -/* virtual */ JSObject* -PresentationAvailability::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationAvailabilityBinding::Wrap(aCx, this, aGivenProto); -} - -bool -PresentationAvailability::Equals(const uint64_t aWindowID, - const nsTArray<nsString>& aUrls) const -{ - if (GetOwner() && GetOwner()->WindowID() == aWindowID && - mUrls.Length() == aUrls.Length()) { - for (const auto& url : aUrls) { - if (!mUrls.Contains(url)) { - return false; - } - } - return true; - } - - return false; -} - -bool -PresentationAvailability::IsCachedValueReady() -{ - // All pending promises will be solved when cached value is ready and - // no promise should be enqueued afterward. - return mPromises.IsEmpty(); -} - -void -PresentationAvailability::EnqueuePromise(RefPtr<Promise>& aPromise) -{ - mPromises.AppendElement(aPromise); -} - -bool -PresentationAvailability::Value() const -{ - return mIsAvailable; -} - -NS_IMETHODIMP -PresentationAvailability::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls, - bool aIsAvailable) -{ - bool available = false; - for (uint32_t i = 0; i < mUrls.Length(); ++i) { - if (aAvailabilityUrls.Contains(mUrls[i])) { - mAvailabilityOfUrl[i] = aIsAvailable; - } - available |= mAvailabilityOfUrl[i]; - } - - return NS_DispatchToCurrentThread(NewRunnableMethod - <bool>(this, - &PresentationAvailability::UpdateAvailabilityAndDispatchEvent, - available)); -} - -void -PresentationAvailability::UpdateAvailabilityAndDispatchEvent(bool aIsAvailable) -{ - PRES_DEBUG("%s\n", __func__); - bool isChanged = (aIsAvailable != mIsAvailable); - - mIsAvailable = aIsAvailable; - - if (!mPromises.IsEmpty()) { - // Use the first availability change notification to resolve promise. - do { - nsTArray<RefPtr<Promise>> promises = Move(mPromises); - for (auto& promise : promises) { - promise->MaybeResolve(this); - } - // more promises may have been added to mPromises, at least in theory - } while (!mPromises.IsEmpty()); - - return; - } - - if (isChanged) { - Unused << - NS_WARN_IF(NS_FAILED(DispatchTrustedEvent(NS_LITERAL_STRING("change")))); - } -} diff --git a/dom/presentation/PresentationAvailability.h b/dom/presentation/PresentationAvailability.h deleted file mode 100644 index edfae2c02..000000000 --- a/dom/presentation/PresentationAvailability.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationAvailability_h -#define mozilla_dom_PresentationAvailability_h - -#include "mozilla/DOMEventTargetHelper.h" -#include "nsIPresentationListener.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { - -class Promise; - -class PresentationAvailability final : public DOMEventTargetHelper - , public nsIPresentationAvailabilityListener - , public SupportsWeakPtr<PresentationAvailability> -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationAvailability, - DOMEventTargetHelper) - NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationAvailability) - - static already_AddRefed<PresentationAvailability> - Create(nsPIDOMWindowInner* aWindow, - const nsTArray<nsString>& aUrls, - RefPtr<Promise>& aPromise); - - virtual void DisconnectFromOwner() override; - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - bool Equals(const uint64_t aWindowID, const nsTArray<nsString>& aUrls) const; - - bool IsCachedValueReady(); - - void EnqueuePromise(RefPtr<Promise>& aPromise); - - // WebIDL (public APIs) - bool Value() const; - - IMPL_EVENT_HANDLER(change); - -private: - explicit PresentationAvailability(nsPIDOMWindowInner* aWindow, - const nsTArray<nsString>& aUrls); - - virtual ~PresentationAvailability(); - - bool Init(RefPtr<Promise>& aPromise); - - void Shutdown(); - - void UpdateAvailabilityAndDispatchEvent(bool aIsAvailable); - - bool mIsAvailable; - - nsTArray<RefPtr<Promise>> mPromises; - - nsTArray<nsString> mUrls; - nsTArray<bool> mAvailabilityOfUrl; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationAvailability_h diff --git a/dom/presentation/PresentationCallbacks.cpp b/dom/presentation/PresentationCallbacks.cpp deleted file mode 100644 index fd0ffee31..000000000 --- a/dom/presentation/PresentationCallbacks.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "mozilla/dom/Promise.h" -#include "nsIDocShell.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIPresentationService.h" -#include "nsIWebProgress.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "PresentationCallbacks.h" -#include "PresentationRequest.h" -#include "PresentationConnection.h" -#include "PresentationTransportBuilderConstructor.h" - -using namespace mozilla; -using namespace mozilla::dom; - -/* - * Implementation of PresentationRequesterCallback - */ - -NS_IMPL_ISUPPORTS(PresentationRequesterCallback, nsIPresentationServiceCallback) - -PresentationRequesterCallback::PresentationRequesterCallback(PresentationRequest* aRequest, - const nsAString& aSessionId, - Promise* aPromise) - : mRequest(aRequest) - , mSessionId(aSessionId) - , mPromise(aPromise) -{ - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mPromise); - MOZ_ASSERT(!mSessionId.IsEmpty()); -} - -PresentationRequesterCallback::~PresentationRequesterCallback() -{ -} - -// nsIPresentationServiceCallback -NS_IMETHODIMP -PresentationRequesterCallback::NotifySuccess(const nsAString& aUrl) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (aUrl.IsEmpty()) { - return NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - RefPtr<PresentationConnection> connection = - PresentationConnection::Create(mRequest->GetOwner(), mSessionId, aUrl, - nsIPresentationService::ROLE_CONTROLLER); - if (NS_WARN_IF(!connection)) { - return NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - mRequest->NotifyPromiseSettled(); - mPromise->MaybeResolve(connection); - - return mRequest->DispatchConnectionAvailableEvent(connection); -} - -NS_IMETHODIMP -PresentationRequesterCallback::NotifyError(nsresult aError) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mRequest->NotifyPromiseSettled(); - mPromise->MaybeReject(aError); - return NS_OK; -} - -/* - * Implementation of PresentationRequesterCallback - */ - -NS_IMPL_ISUPPORTS_INHERITED0(PresentationReconnectCallback, - PresentationRequesterCallback) - -PresentationReconnectCallback::PresentationReconnectCallback( - PresentationRequest* aRequest, - const nsAString& aSessionId, - Promise* aPromise, - PresentationConnection* aConnection) - : PresentationRequesterCallback(aRequest, aSessionId, aPromise) - , mConnection(aConnection) -{ -} - -PresentationReconnectCallback::~PresentationReconnectCallback() -{ -} - -NS_IMETHODIMP -PresentationReconnectCallback::NotifySuccess(const nsAString& aUrl) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsresult rv = NS_OK; - // We found a matched connection with the same window ID, URL, and - // the session ID. Resolve the promise with this connection and dispatch - // the event. - if (mConnection) { - mConnection->NotifyStateChange( - mSessionId, - nsIPresentationSessionListener::STATE_CONNECTING, - NS_OK); - mPromise->MaybeResolve(mConnection); - rv = mRequest->DispatchConnectionAvailableEvent(mConnection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - // Use |PresentationRequesterCallback::NotifySuccess| to create a new - // connection since we don't find one that can be reused. - rv = PresentationRequesterCallback::NotifySuccess(aUrl); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = service->UpdateWindowIdBySessionId(mSessionId, - nsIPresentationService::ROLE_CONTROLLER, - mRequest->GetOwner()->WindowID()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - nsString sessionId = nsString(mSessionId); - return NS_DispatchToMainThread( - NS_NewRunnableFunction([sessionId, service]() -> void { - service->BuildTransport(sessionId, - nsIPresentationService::ROLE_CONTROLLER); - })); -} - -NS_IMETHODIMP -PresentationReconnectCallback::NotifyError(nsresult aError) -{ - if (mConnection) { - mConnection->NotifyStateChange( - mSessionId, - nsIPresentationSessionListener::STATE_CLOSED, - aError); - } - return PresentationRequesterCallback::NotifyError(aError); -} - -NS_IMPL_ISUPPORTS(PresentationResponderLoadingCallback, - nsIWebProgressListener, - nsISupportsWeakReference) - -PresentationResponderLoadingCallback::PresentationResponderLoadingCallback(const nsAString& aSessionId) - : mSessionId(aSessionId) -{ -} - -PresentationResponderLoadingCallback::~PresentationResponderLoadingCallback() -{ - if (mProgress) { - mProgress->RemoveProgressListener(this); - mProgress = nullptr; - } -} - -nsresult -PresentationResponderLoadingCallback::Init(nsIDocShell* aDocShell) -{ - mProgress = do_GetInterface(aDocShell); - if (NS_WARN_IF(!mProgress)) { - return NS_ERROR_NOT_AVAILABLE; - } - - uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE; - nsresult rv = aDocShell->GetBusyFlags(&busyFlags); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if ((busyFlags == nsIDocShell::BUSY_FLAGS_NONE) || - (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING)) { - // The docshell has finished loading or is receiving data (|STATE_TRANSFERRING| - // has already been fired), so the page is ready for presentation use. - return NotifyReceiverReady(/* isLoading = */ true); - } - - // Start to listen to document state change event |STATE_TRANSFERRING|. - return mProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); -} - -nsresult -PresentationResponderLoadingCallback::NotifyReceiverReady(bool aIsLoading) -{ - nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mProgress); - if (NS_WARN_IF(!window || !window->GetCurrentInnerWindow())) { - return NS_ERROR_NOT_AVAILABLE; - } - uint64_t windowId = window->GetCurrentInnerWindow()->WindowID(); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = - PresentationTransportBuilderConstructor::Create(); - return service->NotifyReceiverReady(mSessionId, - windowId,aIsLoading, - constructor); -} - -// nsIWebProgressListener -NS_IMETHODIMP -PresentationResponderLoadingCallback::OnStateChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - uint32_t aStateFlags, - nsresult aStatus) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (aStateFlags & (nsIWebProgressListener::STATE_TRANSFERRING | - nsIWebProgressListener::STATE_STOP)) { - mProgress->RemoveProgressListener(this); - - bool isLoading = aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING; - return NotifyReceiverReady(isLoading); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationResponderLoadingCallback::OnProgressChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - int32_t aCurSelfProgress, - int32_t aMaxSelfProgress, - int32_t aCurTotalProgress, - int32_t aMaxTotalProgress) -{ - // Do nothing. - return NS_OK; -} - -NS_IMETHODIMP -PresentationResponderLoadingCallback::OnLocationChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsIURI* aURI, - uint32_t aFlags) -{ - // Do nothing. - return NS_OK; -} - -NS_IMETHODIMP -PresentationResponderLoadingCallback::OnStatusChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsresult aStatus, - const char16_t* aMessage) -{ - // Do nothing. - return NS_OK; -} - -NS_IMETHODIMP -PresentationResponderLoadingCallback::OnSecurityChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - uint32_t state) -{ - // Do nothing. - return NS_OK; -} diff --git a/dom/presentation/PresentationCallbacks.h b/dom/presentation/PresentationCallbacks.h deleted file mode 100644 index e493b0510..000000000 --- a/dom/presentation/PresentationCallbacks.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationCallbacks_h -#define mozilla_dom_PresentationCallbacks_h - -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "nsIPresentationService.h" -#include "nsIWebProgressListener.h" -#include "nsString.h" -#include "nsWeakReference.h" - -class nsIDocShell; -class nsIWebProgress; - -namespace mozilla { -namespace dom { - -class PresentationConnection; -class PresentationRequest; -class Promise; - -class PresentationRequesterCallback : public nsIPresentationServiceCallback -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSERVICECALLBACK - - PresentationRequesterCallback(PresentationRequest* aRequest, - const nsAString& aSessionId, - Promise* aPromise); - -protected: - virtual ~PresentationRequesterCallback(); - - RefPtr<PresentationRequest> mRequest; - nsString mSessionId; - RefPtr<Promise> mPromise; -}; - -class PresentationReconnectCallback final : public PresentationRequesterCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIPRESENTATIONSERVICECALLBACK - - PresentationReconnectCallback(PresentationRequest* aRequest, - const nsAString& aSessionId, - Promise* aPromise, - PresentationConnection* aConnection); - -private: - virtual ~PresentationReconnectCallback(); - - RefPtr<PresentationConnection> mConnection; -}; - -class PresentationResponderLoadingCallback final : public nsIWebProgressListener - , public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIWEBPROGRESSLISTENER - - explicit PresentationResponderLoadingCallback(const nsAString& aSessionId); - - nsresult Init(nsIDocShell* aDocShell); - -private: - ~PresentationResponderLoadingCallback(); - - nsresult NotifyReceiverReady(bool aIsLoading); - - nsString mSessionId; - nsCOMPtr<nsIWebProgress> mProgress; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationCallbacks_h diff --git a/dom/presentation/PresentationConnection.cpp b/dom/presentation/PresentationConnection.cpp deleted file mode 100644 index e9c4a71ca..000000000 --- a/dom/presentation/PresentationConnection.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "PresentationConnection.h" - -#include "ControllerConnectionCollection.h" -#include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/dom/DOMException.h" -#include "mozilla/dom/File.h" -#include "mozilla/dom/MessageEvent.h" -#include "mozilla/dom/MessageEventBinding.h" -#include "mozilla/dom/PresentationConnectionCloseEvent.h" -#include "mozilla/ErrorNames.h" -#include "mozilla/DebugOnly.h" -#include "nsContentUtils.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIPresentationService.h" -#include "nsServiceManagerUtils.h" -#include "nsStringStream.h" -#include "PresentationConnectionList.h" -#include "PresentationLog.h" - -using namespace mozilla; -using namespace mozilla::dom; - -NS_IMPL_CYCLE_COLLECTION_CLASS(PresentationConnection) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningConnectionList) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PresentationConnection, DOMEventTargetHelper) - tmp->Shutdown(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningConnectionList) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_ADDREF_INHERITED(PresentationConnection, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(PresentationConnection, DOMEventTargetHelper) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnection) - NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionListener) - NS_INTERFACE_MAP_ENTRY(nsIRequest) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -PresentationConnection::PresentationConnection(nsPIDOMWindowInner* aWindow, - const nsAString& aId, - const nsAString& aUrl, - const uint8_t aRole, - PresentationConnectionList* aList) - : DOMEventTargetHelper(aWindow) - , mId(aId) - , mUrl(aUrl) - , mState(PresentationConnectionState::Connecting) - , mOwningConnectionList(aList) - , mBinaryType(PresentationConnectionBinaryType::Arraybuffer) -{ - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - mRole = aRole; -} - -/* virtual */ PresentationConnection::~PresentationConnection() -{ -} - -/* static */ already_AddRefed<PresentationConnection> -PresentationConnection::Create(nsPIDOMWindowInner* aWindow, - const nsAString& aId, - const nsAString& aUrl, - const uint8_t aRole, - PresentationConnectionList* aList) -{ - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - RefPtr<PresentationConnection> connection = - new PresentationConnection(aWindow, aId, aUrl, aRole, aList); - if (NS_WARN_IF(!connection->Init())) { - return nullptr; - } - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - ControllerConnectionCollection::GetSingleton()->AddConnection(connection, - aRole); - } - - return connection.forget(); -} - -bool -PresentationConnection::Init() -{ - if (NS_WARN_IF(mId.IsEmpty())) { - return false; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - return false; - } - - nsresult rv = service->RegisterSessionListener(mId, mRole, this); - if(NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - rv = AddIntoLoadGroup(); - if(NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - return true; -} - -void -PresentationConnection::Shutdown() -{ - PRES_DEBUG("connection shutdown:id[%s], role[%d]\n", - NS_ConvertUTF16toUTF8(mId).get(), mRole); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return; - } - - DebugOnly<nsresult> rv = service->UnregisterSessionListener(mId, mRole); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "UnregisterSessionListener failed"); - - DebugOnly<nsresult> rv2 = RemoveFromLoadGroup(); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "RemoveFromLoadGroup failed"); - - if (mRole == nsIPresentationService::ROLE_CONTROLLER) { - ControllerConnectionCollection::GetSingleton()->RemoveConnection(this, - mRole); - } -} - -/* virtual */ void -PresentationConnection::DisconnectFromOwner() -{ - Unused << NS_WARN_IF(NS_FAILED(ProcessConnectionWentAway())); - DOMEventTargetHelper::DisconnectFromOwner(); -} - -/* virtual */ JSObject* -PresentationConnection::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationConnectionBinding::Wrap(aCx, this, aGivenProto); -} - -void -PresentationConnection::GetId(nsAString& aId) const -{ - aId = mId; -} - -void -PresentationConnection::GetUrl(nsAString& aUrl) const -{ - aUrl = mUrl; -} - -PresentationConnectionState -PresentationConnection::State() const -{ - return mState; -} - -PresentationConnectionBinaryType -PresentationConnection::BinaryType() const -{ - return mBinaryType; -} - -void -PresentationConnection::SetBinaryType(PresentationConnectionBinaryType aType) -{ - mBinaryType = aType; -} - -void -PresentationConnection::Send(const nsAString& aData, - ErrorResult& aRv) -{ - // Sending is not allowed if the session is not connected. - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send message due to an internal error.")); - return; - } - - nsresult rv = service->SendSessionMessage(mId, mRole, aData); - if(NS_WARN_IF(NS_FAILED(rv))) { - const uint32_t kMaxMessageLength = 256; - nsAutoString data(Substring(aData, 0, kMaxMessageLength)); - - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send message: \"") + data + - NS_LITERAL_STRING("\"")); - } -} - -void -PresentationConnection::Send(Blob& aData, - ErrorResult& aRv) -{ - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send message due to an internal error.")); - return; - } - - nsresult rv = service->SendSessionBlob(mId, mRole, &aData); - if(NS_WARN_IF(NS_FAILED(rv))) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send binary message for Blob message.")); - } -} - -void -PresentationConnection::Send(const ArrayBuffer& aData, - ErrorResult& aRv) -{ - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send message due to an internal error.")); - return; - } - - aData.ComputeLengthAndData(); - - static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); - - uint32_t length = aData.Length(); - char* data = reinterpret_cast<char*>(aData.Data()); - nsDependentCSubstring msgString(data, length); - - nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString); - if(NS_WARN_IF(NS_FAILED(rv))) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send binary message for ArrayBuffer message.")); - } -} - -void -PresentationConnection::Send(const ArrayBufferView& aData, - ErrorResult& aRv) -{ - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send message due to an internal error.")); - return; - } - - aData.ComputeLengthAndData(); - - static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required"); - - uint32_t length = aData.Length(); - char* data = reinterpret_cast<char*>(aData.Data()); - nsDependentCSubstring msgString(data, length); - - nsresult rv = service->SendSessionBinaryMsg(mId, mRole, msgString); - if(NS_WARN_IF(NS_FAILED(rv))) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to send binary message for ArrayBufferView message.")); - } -} - -void -PresentationConnection::Close(ErrorResult& aRv) -{ - // It only works when the state is CONNECTED or CONNECTING. - if (NS_WARN_IF(mState != PresentationConnectionState::Connected && - mState != PresentationConnectionState::Connecting)) { - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - aRv.Throw(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - Unused << NS_WARN_IF(NS_FAILED( - service->CloseSession(mId, - mRole, - nsIPresentationService::CLOSED_REASON_CLOSED))); -} - -void -PresentationConnection::Terminate(ErrorResult& aRv) -{ - // It only works when the state is CONNECTED. - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - return; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - aRv.Throw(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - Unused << NS_WARN_IF(NS_FAILED(service->TerminateSession(mId, mRole))); -} - -bool -PresentationConnection::Equals(uint64_t aWindowId, - const nsAString& aId) -{ - return GetOwner() && - aWindowId == GetOwner()->WindowID() && - mId.Equals(aId); -} - -NS_IMETHODIMP -PresentationConnection::NotifyStateChange(const nsAString& aSessionId, - uint16_t aState, - nsresult aReason) -{ - PRES_DEBUG("connection state change:id[%s], state[%x], reason[%x], role[%d]\n", - NS_ConvertUTF16toUTF8(aSessionId).get(), aState, - aReason, mRole); - - if (!aSessionId.Equals(mId)) { - return NS_ERROR_INVALID_ARG; - } - - // A terminated connection should always remain in terminated. - if (mState == PresentationConnectionState::Terminated) { - return NS_OK; - } - - PresentationConnectionState state; - switch (aState) { - case nsIPresentationSessionListener::STATE_CONNECTING: - state = PresentationConnectionState::Connecting; - break; - case nsIPresentationSessionListener::STATE_CONNECTED: - state = PresentationConnectionState::Connected; - break; - case nsIPresentationSessionListener::STATE_CLOSED: - state = PresentationConnectionState::Closed; - break; - case nsIPresentationSessionListener::STATE_TERMINATED: - state = PresentationConnectionState::Terminated; - break; - default: - NS_WARNING("Unknown presentation session state."); - return NS_ERROR_INVALID_ARG; - } - - if (mState == state) { - return NS_OK; - } - mState = state; - - nsresult rv = ProcessStateChanged(aReason); - if(NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mOwningConnectionList) { - mOwningConnectionList->NotifyStateChange(aSessionId, this); - } - - return NS_OK; -} - -nsresult -PresentationConnection::ProcessStateChanged(nsresult aReason) -{ - switch (mState) { - case PresentationConnectionState::Connecting: - return NS_OK; - case PresentationConnectionState::Connected: { - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, NS_LITERAL_STRING("connect"), false); - return asyncDispatcher->PostDOMEvent(); - } - case PresentationConnectionState::Closed: { - PresentationConnectionClosedReason reason = - PresentationConnectionClosedReason::Closed; - - nsString errorMsg; - if (NS_FAILED(aReason)) { - reason = PresentationConnectionClosedReason::Error; - nsCString name, message; - - // If aReason is not a DOM error, use error name as message. - if (NS_FAILED(NS_GetNameAndMessageForDOMNSResult(aReason, - name, - message))) { - mozilla::GetErrorName(aReason, message); - message.InsertLiteral("Internal error: ", 0); - } - CopyUTF8toUTF16(message, errorMsg); - } - - Unused << - NS_WARN_IF(NS_FAILED(DispatchConnectionCloseEvent(reason, errorMsg))); - - return RemoveFromLoadGroup(); - } - case PresentationConnectionState::Terminated: { - // Ensure onterminate event is fired. - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, NS_LITERAL_STRING("terminate"), false); - Unused << NS_WARN_IF(NS_FAILED(asyncDispatcher->PostDOMEvent())); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsresult rv = service->UnregisterSessionListener(mId, mRole); - if(NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return RemoveFromLoadGroup(); - } - default: - MOZ_CRASH("Unknown presentation session state."); - return NS_ERROR_INVALID_ARG; - } -} - -NS_IMETHODIMP -PresentationConnection::NotifyMessage(const nsAString& aSessionId, - const nsACString& aData, - bool aIsBinary) -{ - PRES_DEBUG("connection %s:id[%s], data[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), - nsPromiseFlatCString(aData).get(), mRole); - - if (!aSessionId.Equals(mId)) { - return NS_ERROR_INVALID_ARG; - } - - // No message should be expected when the session is not connected. - if (NS_WARN_IF(mState != PresentationConnectionState::Connected)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (NS_WARN_IF(NS_FAILED(DoReceiveMessage(aData, aIsBinary)))) { - AsyncCloseConnectionWithErrorMsg( - NS_LITERAL_STRING("Unable to receive a message.")); - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -PresentationConnection::DoReceiveMessage(const nsACString& aData, bool aIsBinary) -{ - // Transform the data. - AutoJSAPI jsapi; - if (!jsapi.Init(GetOwner())) { - return NS_ERROR_FAILURE; - } - JSContext* cx = jsapi.cx(); - JS::Rooted<JS::Value> jsData(cx); - - nsresult rv; - if (aIsBinary) { - if (mBinaryType == PresentationConnectionBinaryType::Blob) { - RefPtr<Blob> blob = - Blob::CreateStringBlob(GetOwner(), aData, EmptyString()); - MOZ_ASSERT(blob); - - if (!ToJSValue(cx, blob, &jsData)) { - return NS_ERROR_FAILURE; - } - } else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) { - JS::Rooted<JSObject*> arrayBuf(cx); - rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - jsData.setObject(*arrayBuf); - } else { - NS_RUNTIMEABORT("Unknown binary type!"); - return NS_ERROR_UNEXPECTED; - } - } else { - NS_ConvertUTF8toUTF16 utf16Data(aData); - if(NS_WARN_IF(!ToJSValue(cx, utf16Data, &jsData))) { - return NS_ERROR_FAILURE; - } - } - - return DispatchMessageEvent(jsData); -} - -nsresult -PresentationConnection::DispatchConnectionCloseEvent( - PresentationConnectionClosedReason aReason, - const nsAString& aMessage, - bool aDispatchNow) -{ - if (mState != PresentationConnectionState::Closed) { - MOZ_ASSERT(false, "The connection state should be closed."); - return NS_ERROR_FAILURE; - } - - PresentationConnectionCloseEventInit init; - init.mReason = aReason; - init.mMessage = aMessage; - - RefPtr<PresentationConnectionCloseEvent> closedEvent = - PresentationConnectionCloseEvent::Constructor(this, - NS_LITERAL_STRING("close"), - init); - closedEvent->SetTrusted(true); - - if (aDispatchNow) { - bool ignore; - return DOMEventTargetHelper::DispatchEvent(closedEvent, &ignore); - } - - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, static_cast<Event*>(closedEvent)); - return asyncDispatcher->PostDOMEvent(); -} - -nsresult -PresentationConnection::DispatchMessageEvent(JS::Handle<JS::Value> aData) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - if (NS_WARN_IF(!global)) { - return NS_ERROR_NOT_AVAILABLE; - } - - // Get the origin. - nsAutoString origin; - nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<MessageEvent> messageEvent = new MessageEvent(this, nullptr, nullptr); - - messageEvent->InitMessageEvent(nullptr, - NS_LITERAL_STRING("message"), - false, false, aData, origin, - EmptyString(), nullptr, - Sequence<OwningNonNull<MessagePort>>()); - messageEvent->SetTrusted(true); - - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, static_cast<Event*>(messageEvent)); - return asyncDispatcher->PostDOMEvent(); -} - -nsresult -PresentationConnection::ProcessConnectionWentAway() -{ - if (mState != PresentationConnectionState::Connected && - mState != PresentationConnectionState::Connecting) { - // If the state is not connected or connecting, do not need to - // close the session. - return NS_OK; - } - - mState = PresentationConnectionState::Terminated; - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return service->CloseSession( - mId, mRole, nsIPresentationService::CLOSED_REASON_WENTAWAY); -} - -NS_IMETHODIMP -PresentationConnection::GetName(nsACString &aResult) -{ - aResult.AssignLiteral("about:presentation-connection"); - return NS_OK; -} - -NS_IMETHODIMP -PresentationConnection::IsPending(bool* aRetval) -{ - *aRetval = true; - return NS_OK; -} - -NS_IMETHODIMP -PresentationConnection::GetStatus(nsresult* aStatus) -{ - *aStatus = NS_OK; - return NS_OK; -} - -NS_IMETHODIMP -PresentationConnection::Cancel(nsresult aStatus) -{ - nsCOMPtr<nsIRunnable> event = - NewRunnableMethod(this, &PresentationConnection::ProcessConnectionWentAway); - return NS_DispatchToCurrentThread(event); -} -NS_IMETHODIMP -PresentationConnection::Suspend(void) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} -NS_IMETHODIMP -PresentationConnection::Resume(void) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -PresentationConnection::GetLoadGroup(nsILoadGroup** aLoadGroup) -{ - *aLoadGroup = nullptr; - - nsCOMPtr<nsIDocument> doc = GetOwner() ? GetOwner()->GetExtantDoc() : nullptr; - if (!doc) { - return NS_ERROR_FAILURE; - } - - *aLoadGroup = doc->GetDocumentLoadGroup().take(); - return NS_OK; -} - -NS_IMETHODIMP -PresentationConnection::SetLoadGroup(nsILoadGroup * aLoadGroup) -{ - return NS_ERROR_UNEXPECTED; -} - -NS_IMETHODIMP -PresentationConnection::GetLoadFlags(nsLoadFlags* aLoadFlags) -{ - *aLoadFlags = nsIRequest::LOAD_BACKGROUND; - return NS_OK; -} - -NS_IMETHODIMP -PresentationConnection::SetLoadFlags(nsLoadFlags aLoadFlags) -{ - return NS_OK; -} - -nsresult -PresentationConnection::AddIntoLoadGroup() -{ - // Avoid adding to loadgroup multiple times - if (mWeakLoadGroup) { - return NS_OK; - } - - nsCOMPtr<nsILoadGroup> loadGroup; - nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup)); - if(NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = loadGroup->AddRequest(this, nullptr); - if(NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mWeakLoadGroup = do_GetWeakReference(loadGroup); - return NS_OK; -} - -nsresult -PresentationConnection::RemoveFromLoadGroup() -{ - if (!mWeakLoadGroup) { - return NS_OK; - } - - nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup); - if (loadGroup) { - mWeakLoadGroup = nullptr; - return loadGroup->RemoveRequest(this, nullptr, NS_OK); - } - - return NS_OK; -} - -void -PresentationConnection::AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage) -{ - if (mState == PresentationConnectionState::Terminated) { - return; - } - - nsString message = nsString(aMessage); - RefPtr<PresentationConnection> self = this; - nsCOMPtr<nsIRunnable> r = - NS_NewRunnableFunction([self, message]() -> void { - // Set |mState| to |PresentationConnectionState::Closed| here to avoid - // calling |ProcessStateChanged|. - self->mState = PresentationConnectionState::Closed; - - // Make sure dispatching the event and closing the connection are invoked - // at the same time by setting |aDispatchNow| to true. - Unused << NS_WARN_IF(NS_FAILED( - self->DispatchConnectionCloseEvent(PresentationConnectionClosedReason::Error, - message, - true))); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - return; - } - - Unused << NS_WARN_IF(NS_FAILED( - service->CloseSession(self->mId, - self->mRole, - nsIPresentationService::CLOSED_REASON_ERROR))); - }); - - Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r))); -} diff --git a/dom/presentation/PresentationConnection.h b/dom/presentation/PresentationConnection.h deleted file mode 100644 index cecf6c346..000000000 --- a/dom/presentation/PresentationConnection.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationConnection_h -#define mozilla_dom_PresentationConnection_h - -#include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/dom/TypedArray.h" -#include "mozilla/WeakPtr.h" -#include "mozilla/dom/PresentationConnectionBinding.h" -#include "mozilla/dom/PresentationConnectionCloseEventBinding.h" -#include "nsIPresentationListener.h" -#include "nsIRequest.h" -#include "nsWeakReference.h" - -namespace mozilla { -namespace dom { - -class Blob; -class PresentationConnectionList; - -class PresentationConnection final : public DOMEventTargetHelper - , public nsIPresentationSessionListener - , public nsIRequest - , public SupportsWeakPtr<PresentationConnection> -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnection, - DOMEventTargetHelper) - NS_DECL_NSIPRESENTATIONSESSIONLISTENER - NS_DECL_NSIREQUEST - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PresentationConnection) - - static already_AddRefed<PresentationConnection> - Create(nsPIDOMWindowInner* aWindow, - const nsAString& aId, - const nsAString& aUrl, - const uint8_t aRole, - PresentationConnectionList* aList = nullptr); - - virtual void DisconnectFromOwner() override; - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - // WebIDL (public APIs) - void GetId(nsAString& aId) const; - - void GetUrl(nsAString& aUrl) const; - - PresentationConnectionState State() const; - - PresentationConnectionBinaryType BinaryType() const; - - void SetBinaryType(PresentationConnectionBinaryType aType); - - void Send(const nsAString& aData, - ErrorResult& aRv); - - void Send(Blob& aData, - ErrorResult& aRv); - - void Send(const ArrayBuffer& aData, - ErrorResult& aRv); - - void Send(const ArrayBufferView& aData, - ErrorResult& aRv); - - void Close(ErrorResult& aRv); - - void Terminate(ErrorResult& aRv); - - bool - Equals(uint64_t aWindowId, const nsAString& aId); - - IMPL_EVENT_HANDLER(connect); - IMPL_EVENT_HANDLER(close); - IMPL_EVENT_HANDLER(terminate); - IMPL_EVENT_HANDLER(message); - -private: - PresentationConnection(nsPIDOMWindowInner* aWindow, - const nsAString& aId, - const nsAString& aUrl, - const uint8_t aRole, - PresentationConnectionList* aList); - - ~PresentationConnection(); - - bool Init(); - - void Shutdown(); - - nsresult ProcessStateChanged(nsresult aReason); - - nsresult DispatchConnectionCloseEvent(PresentationConnectionClosedReason aReason, - const nsAString& aMessage, - bool aDispatchNow = false); - - nsresult DispatchMessageEvent(JS::Handle<JS::Value> aData); - - nsresult ProcessConnectionWentAway(); - - nsresult AddIntoLoadGroup(); - - nsresult RemoveFromLoadGroup(); - - void AsyncCloseConnectionWithErrorMsg(const nsAString& aMessage); - - nsresult DoReceiveMessage(const nsACString& aData, bool aIsBinary); - - nsString mId; - nsString mUrl; - uint8_t mRole; - PresentationConnectionState mState; - RefPtr<PresentationConnectionList> mOwningConnectionList; - nsWeakPtr mWeakLoadGroup; - PresentationConnectionBinaryType mBinaryType; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationConnection_h diff --git a/dom/presentation/PresentationConnectionList.cpp b/dom/presentation/PresentationConnectionList.cpp deleted file mode 100644 index 0e0e7696c..000000000 --- a/dom/presentation/PresentationConnectionList.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "PresentationConnectionList.h" - -#include "mozilla/AsyncEventDispatcher.h" -#include "mozilla/dom/PresentationConnectionAvailableEvent.h" -#include "mozilla/dom/PresentationConnectionListBinding.h" -#include "mozilla/dom/Promise.h" -#include "PresentationConnection.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_INHERITED(PresentationConnectionList, DOMEventTargetHelper, - mGetConnectionListPromise, - mConnections) - -NS_IMPL_ADDREF_INHERITED(PresentationConnectionList, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(PresentationConnectionList, DOMEventTargetHelper) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationConnectionList) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -PresentationConnectionList::PresentationConnectionList(nsPIDOMWindowInner* aWindow, - Promise* aPromise) - : DOMEventTargetHelper(aWindow) - , mGetConnectionListPromise(aPromise) -{ - MOZ_ASSERT(aWindow); - MOZ_ASSERT(aPromise); -} - -/* virtual */ JSObject* -PresentationConnectionList::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationConnectionListBinding::Wrap(aCx, this, aGivenProto); -} - -void -PresentationConnectionList::GetConnections( - nsTArray<RefPtr<PresentationConnection>>& aConnections) const -{ - aConnections = mConnections; -} - -nsresult -PresentationConnectionList::DispatchConnectionAvailableEvent( - PresentationConnection* aConnection) -{ - PresentationConnectionAvailableEventInit init; - init.mConnection = aConnection; - - RefPtr<PresentationConnectionAvailableEvent> event = - PresentationConnectionAvailableEvent::Constructor( - this, - NS_LITERAL_STRING("connectionavailable"), - init); - - if (NS_WARN_IF(!event)) { - return NS_ERROR_FAILURE; - } - event->SetTrusted(true); - - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, event); - return asyncDispatcher->PostDOMEvent(); -} - -PresentationConnectionList::ConnectionArrayIndex -PresentationConnectionList::FindConnectionById( - const nsAString& aId) -{ - for (ConnectionArrayIndex i = 0; i < mConnections.Length(); i++) { - nsAutoString id; - mConnections[i]->GetId(id); - if (id == nsAutoString(aId)) { - return i; - } - } - - return mConnections.NoIndex; -} - -void -PresentationConnectionList::NotifyStateChange(const nsAString& aSessionId, - PresentationConnection* aConnection) -{ - if (!aConnection) { - MOZ_ASSERT(false, "PresentationConnection can not be null."); - return; - } - - bool connectionFound = - FindConnectionById(aSessionId) != mConnections.NoIndex ? true : false; - - PresentationConnectionListBinding::ClearCachedConnectionsValue(this); - switch (aConnection->State()) { - case PresentationConnectionState::Connected: - if (!connectionFound) { - mConnections.AppendElement(aConnection); - if (mGetConnectionListPromise) { - mGetConnectionListPromise->MaybeResolve(this); - mGetConnectionListPromise = nullptr; - return; - } - } - DispatchConnectionAvailableEvent(aConnection); - break; - case PresentationConnectionState::Terminated: - if (connectionFound) { - mConnections.RemoveElement(aConnection); - } - break; - default: - break; - } -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationConnectionList.h b/dom/presentation/PresentationConnectionList.h deleted file mode 100644 index b430219ce..000000000 --- a/dom/presentation/PresentationConnectionList.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationConnectionList_h -#define mozilla_dom_PresentationConnectionList_h - -#include "mozilla/DOMEventTargetHelper.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { - -class PresentationConnection; -class Promise; - -class PresentationConnectionList final : public DOMEventTargetHelper -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PresentationConnectionList, - DOMEventTargetHelper) - - PresentationConnectionList(nsPIDOMWindowInner* aWindow, - Promise* aPromise); - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - void GetConnections(nsTArray<RefPtr<PresentationConnection>>& aConnections) const; - - void NotifyStateChange(const nsAString& aSessionId, PresentationConnection* aConnection); - - IMPL_EVENT_HANDLER(connectionavailable); - -private: - virtual ~PresentationConnectionList() = default; - - nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection); - - typedef nsTArray<RefPtr<PresentationConnection>> ConnectionArray; - typedef ConnectionArray::index_type ConnectionArrayIndex; - - ConnectionArrayIndex FindConnectionById(const nsAString& aId); - - RefPtr<Promise> mGetConnectionListPromise; - - // This array stores only non-terminsted connections. - ConnectionArray mConnections; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationConnectionList_h diff --git a/dom/presentation/PresentationDataChannelSessionTransport.js b/dom/presentation/PresentationDataChannelSessionTransport.js deleted file mode 100644 index 9af6213cb..000000000 --- a/dom/presentation/PresentationDataChannelSessionTransport.js +++ /dev/null @@ -1,378 +0,0 @@ -/* 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/. */ - -"use strict"; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// Bug 1228209 - plan to remove this eventually -function log(aMsg) { - //dump("-*- PresentationDataChannelSessionTransport.js : " + aMsg + "\n"); -} - -const PRESENTATIONTRANSPORT_CID = Components.ID("{dd2bbf2f-3399-4389-8f5f-d382afb8b2d6}"); -const PRESENTATIONTRANSPORT_CONTRACTID = "mozilla.org/presentation/datachanneltransport;1"; - -const PRESENTATIONTRANSPORTBUILDER_CID = Components.ID("{215b2f62-46e2-4004-a3d1-6858e56c20f3}"); -const PRESENTATIONTRANSPORTBUILDER_CONTRACTID = "mozilla.org/presentation/datachanneltransportbuilder;1"; - -function PresentationDataChannelDescription(aDataChannelSDP) { - this._dataChannelSDP = JSON.stringify(aDataChannelSDP); -} - -PresentationDataChannelDescription.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), - get type() { - return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL; - }, - get tcpAddress() { - return null; - }, - get tcpPort() { - return null; - }, - get dataChannelSDP() { - return this._dataChannelSDP; - } -}; - -function PresentationTransportBuilder() { - log("PresentationTransportBuilder construct"); - this._isControlChannelNeeded = true; -} - -PresentationTransportBuilder.prototype = { - classID: PRESENTATIONTRANSPORTBUILDER_CID, - contractID: PRESENTATIONTRANSPORTBUILDER_CONTRACTID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilder, - Ci.nsIPresentationDataChannelSessionTransportBuilder, - Ci.nsITimerCallback]), - - buildDataChannelTransport: function(aRole, aWindow, aListener) { - if (!aRole || !aWindow || !aListener) { - log("buildDataChannelTransport with illegal parameters"); - throw Cr.NS_ERROR_ILLEGAL_VALUE; - } - - if (this._window) { - log("buildDataChannelTransport has started."); - throw Cr.NS_ERROR_UNEXPECTED; - } - - log("buildDataChannelTransport with role " + aRole); - this._role = aRole; - this._window = aWindow; - this._listener = aListener.QueryInterface(Ci.nsIPresentationSessionTransportBuilderListener); - - // TODO bug 1227053 set iceServers from |nsIPresentationDevice| - this._peerConnection = new this._window.RTCPeerConnection(); - - // |this._listener == null| will throw since the control channel is - // abnormally closed. - this._peerConnection.onicecandidate = aEvent => aEvent.candidate && - this._listener.sendIceCandidate(JSON.stringify(aEvent.candidate)); - - this._peerConnection.onnegotiationneeded = () => { - log("onnegotiationneeded with role " + this._role); - if (!this._peerConnection) { - log("ignoring negotiationneeded without PeerConnection"); - return; - } - this._peerConnection.createOffer() - .then(aOffer => this._peerConnection.setLocalDescription(aOffer)) - .then(() => this._listener - .sendOffer(new PresentationDataChannelDescription(this._peerConnection.localDescription))) - .catch(e => this._reportError(e)); - } - - switch (this._role) { - case Ci.nsIPresentationService.ROLE_CONTROLLER: - this._dataChannel = this._peerConnection.createDataChannel("presentationAPI"); - this._setDataChannel(); - break; - - case Ci.nsIPresentationService.ROLE_RECEIVER: - this._peerConnection.ondatachannel = aEvent => { - this._dataChannel = aEvent.channel; - // Ensure the binaryType of dataChannel is blob. - this._dataChannel.binaryType = "blob"; - this._setDataChannel(); - } - break; - default: - throw Cr.NS_ERROR_ILLEGAL_VALUE; - } - - // TODO bug 1228235 we should have a way to let device providers customize - // the time-out duration. - let timeout = Services.prefs.getIntPref("presentation.receiver.loading.timeout", 10000); - - // The timer is to check if the negotiation finishes on time. - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._timer.initWithCallback(this, timeout, this._timer.TYPE_ONE_SHOT); - }, - - notify: function() { - if (!this._sessionTransport) { - this._cleanup(Cr.NS_ERROR_NET_TIMEOUT); - } - }, - - _reportError: function(aError) { - log("report Error " + aError.name + ":" + aError.message); - this._cleanup(Cr.NS_ERROR_FAILURE); - }, - - _setDataChannel: function() { - this._dataChannel.onopen = () => { - log("data channel is open, notify the listener, role " + this._role); - - // Handoff the ownership of _peerConnection and _dataChannel to - // _sessionTransport - this._sessionTransport = new PresentationTransport(); - this._sessionTransport.init(this._peerConnection, this._dataChannel, this._window); - this._peerConnection.onicecandidate = null; - this._peerConnection.onnegotiationneeded = null; - this._peerConnection = this._dataChannel = null; - - this._listener.onSessionTransport(this._sessionTransport); - this._sessionTransport.callback.notifyTransportReady(); - - this._cleanup(Cr.NS_OK); - }; - - this._dataChannel.onerror = aError => { - log("data channel onerror " + aError.name + ":" + aError.message); - this._cleanup(Cr.NS_ERROR_FAILURE); - } - }, - - _cleanup: function(aReason) { - if (aReason != Cr.NS_OK) { - this._listener.onError(aReason); - } - - if (this._dataChannel) { - this._dataChannel.close(); - this._dataChannel = null; - } - - if (this._peerConnection) { - this._peerConnection.close(); - this._peerConnection = null; - } - - this._role = null; - this._window = null; - - this._listener = null; - this._sessionTransport = null; - - if (this._timer) { - this._timer.cancel(); - this._timer = null; - } - }, - - // nsIPresentationControlChannelListener - onOffer: function(aOffer) { - if (this._role !== Ci.nsIPresentationService.ROLE_RECEIVER || - this._sessionTransport) { - log("onOffer status error"); - this._cleanup(Cr.NS_ERROR_FAILURE); - } - - log("onOffer: " + aOffer.dataChannelSDP + " with role " + this._role); - - let offer = new this._window - .RTCSessionDescription(JSON.parse(aOffer.dataChannelSDP)); - - this._peerConnection.setRemoteDescription(offer) - .then(() => this._peerConnection.signalingState == "stable" || - this._peerConnection.createAnswer()) - .then(aAnswer => this._peerConnection.setLocalDescription(aAnswer)) - .then(() => { - this._isControlChannelNeeded = false; - this._listener - .sendAnswer(new PresentationDataChannelDescription(this._peerConnection.localDescription)) - }).catch(e => this._reportError(e)); - }, - - onAnswer: function(aAnswer) { - if (this._role !== Ci.nsIPresentationService.ROLE_CONTROLLER || - this._sessionTransport) { - log("onAnswer status error"); - this._cleanup(Cr.NS_ERROR_FAILURE); - } - - log("onAnswer: " + aAnswer.dataChannelSDP + " with role " + this._role); - - let answer = new this._window - .RTCSessionDescription(JSON.parse(aAnswer.dataChannelSDP)); - - this._peerConnection.setRemoteDescription(answer).catch(e => this._reportError(e)); - this._isControlChannelNeeded = false; - }, - - onIceCandidate: function(aCandidate) { - log("onIceCandidate: " + aCandidate + " with role " + this._role); - if (!this._window || !this._peerConnection) { - log("ignoring ICE candidate after connection"); - return; - } - let candidate = new this._window.RTCIceCandidate(JSON.parse(aCandidate)); - this._peerConnection.addIceCandidate(candidate).catch(e => this._reportError(e)); - }, - - notifyDisconnected: function(aReason) { - log("notifyDisconnected reason: " + aReason); - - if (aReason != Cr.NS_OK) { - this._cleanup(aReason); - } else if (this._isControlChannelNeeded) { - this._cleanup(Cr.NS_ERROR_FAILURE); - } - }, -}; - -function PresentationTransport() { - this._messageQueue = []; - this._closeReason = Cr.NS_OK; -} - -PresentationTransport.prototype = { - classID: PRESENTATIONTRANSPORT_CID, - contractID: PRESENTATIONTRANSPORT_CONTRACTID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport]), - - init: function(aPeerConnection, aDataChannel, aWindow) { - log("initWithDataChannel"); - this._enableDataNotification = false; - this._dataChannel = aDataChannel; - this._peerConnection = aPeerConnection; - this._window = aWindow; - - this._dataChannel.onopen = () => { - log("data channel reopen. Should never touch here"); - }; - - this._dataChannel.onclose = () => { - log("data channel onclose"); - if (this._callback) { - this._callback.notifyTransportClosed(this._closeReason); - } - this._cleanup(); - } - - this._dataChannel.onmessage = aEvent => { - log("data channel onmessage " + aEvent.data); - - if (!this._enableDataNotification || !this._callback) { - log("queue message"); - this._messageQueue.push(aEvent.data); - return; - } - this._doNotifyData(aEvent.data); - }; - - this._dataChannel.onerror = aError => { - log("data channel onerror " + aError.name + ":" + aError.message); - if (this._callback) { - this._callback.notifyTransportClosed(Cr.NS_ERROR_FAILURE); - } - this._cleanup(); - } - }, - - // nsIPresentationTransport - get selfAddress() { - throw NS_ERROR_NOT_AVAILABLE; - }, - - get callback() { - return this._callback; - }, - - set callback(aCallback) { - this._callback = aCallback; - }, - - send: function(aData) { - log("send " + aData); - this._dataChannel.send(aData); - }, - - sendBinaryMsg: function(aData) { - log("sendBinaryMsg"); - - let array = new Uint8Array(aData.length); - for (let i = 0; i < aData.length; i++) { - array[i] = aData.charCodeAt(i); - } - - this._dataChannel.send(array); - }, - - sendBlob: function(aBlob) { - log("sendBlob"); - - this._dataChannel.send(aBlob); - }, - - enableDataNotification: function() { - log("enableDataNotification"); - if (this._enableDataNotification) { - return; - } - - if (!this._callback) { - throw NS_ERROR_NOT_AVAILABLE; - } - - this._enableDataNotification = true; - - this._messageQueue.forEach(aData => this._doNotifyData(aData)); - this._messageQueue = []; - }, - - close: function(aReason) { - this._closeReason = aReason; - - this._dataChannel.close(); - }, - - _cleanup: function() { - this._dataChannel = null; - - if (this._peerConnection) { - this._peerConnection.close(); - this._peerConnection = null; - } - this._callback = null; - this._messageQueue = []; - this._window = null; - }, - - _doNotifyData: function(aData) { - if (!this._callback) { - throw NS_ERROR_NOT_AVAILABLE; - } - - if (aData instanceof this._window.Blob) { - let reader = new this._window.FileReader(); - reader.addEventListener("load", (aEvent) => { - this._callback.notifyData(aEvent.target.result, true); - }); - reader.readAsBinaryString(aData); - } else { - this._callback.notifyData(aData, false); - } - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationTransportBuilder, - PresentationTransport]); diff --git a/dom/presentation/PresentationDataChannelSessionTransport.manifest b/dom/presentation/PresentationDataChannelSessionTransport.manifest deleted file mode 100644 index 6838f675f..000000000 --- a/dom/presentation/PresentationDataChannelSessionTransport.manifest +++ /dev/null @@ -1,6 +0,0 @@ -# PresentationDataChannelSessionTransport.js -component {dd2bbf2f-3399-4389-8f5f-d382afb8b2d6} PresentationDataChannelSessionTransport.js -contract @mozilla.org/presentation/datachanneltransport;1 {dd2bbf2f-3399-4389-8f5f-d382afb8b2d6} - -component {215b2f62-46e2-4004-a3d1-6858e56c20f3} PresentationDataChannelSessionTransport.js -contract @mozilla.org/presentation/datachanneltransportbuilder;1 {215b2f62-46e2-4004-a3d1-6858e56c20f3} diff --git a/dom/presentation/PresentationDeviceInfoManager.js b/dom/presentation/PresentationDeviceInfoManager.js deleted file mode 100644 index 29e7d370c..000000000 --- a/dom/presentation/PresentationDeviceInfoManager.js +++ /dev/null @@ -1,119 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); - -function log(aMsg) { - //dump("-*- PresentationDeviceInfoManager.js : " + aMsg + "\n"); -} - -const PRESENTATIONDEVICEINFOMANAGER_CID = Components.ID("{1bd66bef-f643-4be3-b690-0c656353eafd}"); -const PRESENTATIONDEVICEINFOMANAGER_CONTRACTID = "@mozilla.org/presentation-device/deviceInfo;1"; - -XPCOMUtils.defineLazyServiceGetter(this, "cpmm", - "@mozilla.org/childprocessmessagemanager;1", - "nsIMessageSender"); - -function PresentationDeviceInfoManager() {} - -PresentationDeviceInfoManager.prototype = { - __proto__: DOMRequestIpcHelper.prototype, - - classID: PRESENTATIONDEVICEINFOMANAGER_CID, - contractID: PRESENTATIONDEVICEINFOMANAGER_CONTRACTID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, - Ci.nsIObserver, - Ci.nsIDOMGlobalPropertyInitializer]), - - receiveMessage: function(aMsg) { - if (!aMsg || !aMsg.data) { - return; - } - - let data = aMsg.data; - - log("receive aMsg: " + aMsg.name); - switch (aMsg.name) { - case "PresentationDeviceInfoManager:OnDeviceChange": { - let detail = { - detail: { - type: data.type, - deviceInfo: data.deviceInfo, - } - }; - let event = new this._window.CustomEvent("devicechange", Cu.cloneInto(detail, this._window)); - this.__DOM_IMPL__.dispatchEvent(event); - break; - } - case "PresentationDeviceInfoManager:GetAll:Result:Ok": { - let resolver = this.takePromiseResolver(data.requestId); - - if (!resolver) { - return; - } - - resolver.resolve(Cu.cloneInto(data.devices, this._window)); - break; - } - case "PresentationDeviceInfoManager:GetAll:Result:Error": { - let resolver = this.takePromiseResolver(data.requestId); - - if (!resolver) { - return; - } - - resolver.reject(data.error); - break; - } - } - }, - - init: function(aWin) { - log("init"); - this.initDOMRequestHelper(aWin, [ - {name: "PresentationDeviceInfoManager:OnDeviceChange", weakRef: true}, - {name: "PresentationDeviceInfoManager:GetAll:Result:Ok", weakRef: true}, - {name: "PresentationDeviceInfoManager:GetAll:Result:Error", weakRef: true}, - ]); - }, - - uninit: function() { - log("uninit"); - let self = this; - - this.forEachPromiseResolver(function(aKey) { - self.takePromiseResolver(aKey).reject("PresentationDeviceInfoManager got destroyed"); - }); - }, - - get ondevicechange() { - return this.__DOM_IMPL__.getEventHandler("ondevicechange"); - }, - - set ondevicechange(aHandler) { - this.__DOM_IMPL__.setEventHandler("ondevicechange", aHandler); - }, - - getAll: function() { - log("getAll"); - let self = this; - return this.createPromiseWithId(function(aResolverId) { - cpmm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll", { - requestId: aResolverId, - }); - }); - }, - - forceDiscovery: function() { - cpmm.sendAsyncMessage("PresentationDeviceInfoManager:ForceDiscovery"); - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationDeviceInfoManager]); diff --git a/dom/presentation/PresentationDeviceInfoManager.jsm b/dom/presentation/PresentationDeviceInfoManager.jsm deleted file mode 100644 index 205982b9c..000000000 --- a/dom/presentation/PresentationDeviceInfoManager.jsm +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- / -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = ["PresentationDeviceInfoService"]; - -function log(aMsg) { - //dump("PresentationDeviceInfoManager.jsm: " + aMsg + "\n"); -} - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "presentationDeviceManager", - "@mozilla.org/presentation-device/manager;1", - "nsIPresentationDeviceManager"); - -XPCOMUtils.defineLazyServiceGetter(this, "ppmm", - "@mozilla.org/parentprocessmessagemanager;1", - "nsIMessageBroadcaster"); - -this.PresentationDeviceInfoService = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener, - Ci.nsIObserver]), - - init: function() { - log("init"); - ppmm.addMessageListener("PresentationDeviceInfoManager:GetAll", this); - ppmm.addMessageListener("PresentationDeviceInfoManager:ForceDiscovery", this); - Services.obs.addObserver(this, "presentation-device-change", false); - }, - - getAll: function(aData, aMm) { - log("getAll"); - let deviceArray = presentationDeviceManager.getAvailableDevices().QueryInterface(Ci.nsIArray); - if (!deviceArray) { - aData.error = "DataError"; - aMm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll:Result:Error", aData); - return; - } - - aData.devices = []; - for (let i = 0; i < deviceArray.length; i++) { - let device = deviceArray.queryElementAt(i, Ci.nsIPresentationDevice); - aData.devices.push({ - id: device.id, - name: device.name, - type: device.type, - }); - } - aMm.sendAsyncMessage("PresentationDeviceInfoManager:GetAll:Result:Ok", aData); - }, - - forceDiscovery: function() { - log("forceDiscovery"); - presentationDeviceManager.forceDiscovery(); - }, - - observe: function(aSubject, aTopic, aData) { - log("observe: " + aTopic); - - let device = aSubject.QueryInterface(Ci.nsIPresentationDevice); - let data = { - type: aData, - deviceInfo: { - id: device.id, - name: device.name, - type: device.type, - }, - }; - ppmm.broadcastAsyncMessage("PresentationDeviceInfoManager:OnDeviceChange", data); - }, - - receiveMessage: function(aMessage) { - if (!aMessage.target.assertPermission("presentation-device-manage")) { - debug("receive message " + aMessage.name + - " from a content process with no 'presentation-device-manage' privileges."); - return null; - } - - let msg = aMessage.data || {}; - let mm = aMessage.target; - - log("receiveMessage: " + aMessage.name); - switch (aMessage.name) { - case "PresentationDeviceInfoManager:GetAll": { - this.getAll(msg, mm); - break; - } - case "PresentationDeviceInfoManager:ForceDiscovery": { - this.forceDiscovery(); - break; - } - } - }, -}; - -this.PresentationDeviceInfoService.init(); diff --git a/dom/presentation/PresentationDeviceInfoManager.manifest b/dom/presentation/PresentationDeviceInfoManager.manifest deleted file mode 100644 index ae50b8e6a..000000000 --- a/dom/presentation/PresentationDeviceInfoManager.manifest +++ /dev/null @@ -1,3 +0,0 @@ -# PresentationDeviceInfoManager.js -component {1bd66bef-f643-4be3-b690-0c656353eafd} PresentationDeviceInfoManager.js -contract @mozilla.org/presentation-device/deviceInfo;1 {1bd66bef-f643-4be3-b690-0c656353eafd} diff --git a/dom/presentation/PresentationDeviceManager.cpp b/dom/presentation/PresentationDeviceManager.cpp deleted file mode 100644 index 7e5a4700c..000000000 --- a/dom/presentation/PresentationDeviceManager.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* -*- 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 "PresentationDeviceManager.h" - -#include "mozilla/Services.h" -#include "MainThreadUtils.h" -#include "nsArrayUtils.h" -#include "nsCategoryCache.h" -#include "nsCOMPtr.h" -#include "nsIMutableArray.h" -#include "nsIObserverService.h" -#include "nsXULAppAPI.h" -#include "PresentationSessionRequest.h" -#include "PresentationTerminateRequest.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(PresentationDeviceManager, - nsIPresentationDeviceManager, - nsIPresentationDeviceListener, - nsIObserver, - nsISupportsWeakReference) - -PresentationDeviceManager::PresentationDeviceManager() -{ -} - -PresentationDeviceManager::~PresentationDeviceManager() -{ - UnloadDeviceProviders(); - mDevices.Clear(); -} - -void -PresentationDeviceManager::Init() -{ - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); - } - - LoadDeviceProviders(); -} - -void -PresentationDeviceManager::Shutdown() -{ - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - } - - UnloadDeviceProviders(); -} - -void -PresentationDeviceManager::LoadDeviceProviders() -{ - MOZ_ASSERT(mProviders.IsEmpty()); - - nsCategoryCache<nsIPresentationDeviceProvider> providerCache(PRESENTATION_DEVICE_PROVIDER_CATEGORY); - providerCache.GetEntries(mProviders); - - for (uint32_t i = 0; i < mProviders.Length(); ++i) { - mProviders[i]->SetListener(this); - } -} - -void -PresentationDeviceManager::UnloadDeviceProviders() -{ - for (uint32_t i = 0; i < mProviders.Length(); ++i) { - mProviders[i]->SetListener(nullptr); - } - - mProviders.Clear(); -} - -void -PresentationDeviceManager::NotifyDeviceChange(nsIPresentationDevice* aDevice, - const char16_t* aType) -{ - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (obs) { - obs->NotifyObservers(aDevice, - PRESENTATION_DEVICE_CHANGE_TOPIC, - aType); - } -} - -// nsIPresentationDeviceManager -NS_IMETHODIMP -PresentationDeviceManager::ForceDiscovery() -{ - MOZ_ASSERT(NS_IsMainThread()); - - for (uint32_t i = 0; i < mProviders.Length(); ++i) { - mProviders[i]->ForceDiscovery(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::AddDeviceProvider(nsIPresentationDeviceProvider* aProvider) -{ - NS_ENSURE_ARG(aProvider); - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(mProviders.Contains(aProvider))) { - return NS_OK; - } - - mProviders.AppendElement(aProvider); - aProvider->SetListener(this); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::RemoveDeviceProvider(nsIPresentationDeviceProvider* aProvider) -{ - NS_ENSURE_ARG(aProvider); - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!mProviders.RemoveElement(aProvider))) { - return NS_ERROR_FAILURE; - } - - aProvider->SetListener(nullptr); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::GetDeviceAvailable(bool* aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - MOZ_ASSERT(NS_IsMainThread()); - - *aRetVal = !mDevices.IsEmpty(); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::GetAvailableDevices(nsIArray* aPresentationUrls, nsIArray** aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - MOZ_ASSERT(NS_IsMainThread()); - - // Bug 1194049: some providers may discontinue discovery after timeout. - // Call |ForceDiscovery()| here to make sure device lists are updated. - NS_DispatchToMainThread( - NewRunnableMethod(this, &PresentationDeviceManager::ForceDiscovery)); - - nsTArray<nsString> presentationUrls; - if (aPresentationUrls) { - uint32_t length; - nsresult rv = aPresentationUrls->GetLength(&length); - if (NS_SUCCEEDED(rv)) { - for (uint32_t i = 0; i < length; ++i) { - nsCOMPtr<nsISupportsString> isupportStr = - do_QueryElementAt(aPresentationUrls, i); - - nsAutoString presentationUrl; - rv = isupportStr->GetData(presentationUrl); - if (NS_WARN_IF(NS_FAILED(rv))) { - continue; - } - - presentationUrls.AppendElement(presentationUrl); - } - } - } - - nsCOMPtr<nsIMutableArray> devices = do_CreateInstance(NS_ARRAY_CONTRACTID); - for (uint32_t i = 0; i < mDevices.Length(); ++i) { - if (presentationUrls.IsEmpty()) { - devices->AppendElement(mDevices[i], false); - continue; - } - - for (uint32_t j = 0; j < presentationUrls.Length(); ++j) { - bool isSupported; - if (NS_SUCCEEDED(mDevices[i]->IsRequestedUrlSupported(presentationUrls[j], - &isSupported)) && - isSupported) { - devices->AppendElement(mDevices[i], false); - break; - } - } - } - - devices.forget(aRetVal); - - return NS_OK; -} - -// nsIPresentationDeviceListener -NS_IMETHODIMP -PresentationDeviceManager::AddDevice(nsIPresentationDevice* aDevice) -{ - NS_ENSURE_ARG(aDevice); - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(mDevices.Contains(aDevice))) { - return NS_ERROR_FAILURE; - } - - mDevices.AppendElement(aDevice); - - NotifyDeviceChange(aDevice, u"add"); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::RemoveDevice(nsIPresentationDevice* aDevice) -{ - NS_ENSURE_ARG(aDevice); - MOZ_ASSERT(NS_IsMainThread()); - - int32_t index = mDevices.IndexOf(aDevice); - if (NS_WARN_IF(index < 0)) { - return NS_ERROR_FAILURE; - } - - mDevices.RemoveElementAt(index); - - NotifyDeviceChange(aDevice, u"remove"); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::UpdateDevice(nsIPresentationDevice* aDevice) -{ - NS_ENSURE_ARG(aDevice); - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!mDevices.Contains(aDevice))) { - return NS_ERROR_FAILURE; - } - - NotifyDeviceChange(aDevice, u"update"); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::OnSessionRequest(nsIPresentationDevice* aDevice, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - NS_ENSURE_ARG(aDevice); - NS_ENSURE_ARG(aControlChannel); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); - - RefPtr<PresentationSessionRequest> request = - new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel); - obs->NotifyObservers(request, - PRESENTATION_SESSION_REQUEST_TOPIC, - nullptr); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::OnTerminateRequest(nsIPresentationDevice* aDevice, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel, - bool aIsFromReceiver) -{ - NS_ENSURE_ARG(aDevice); - NS_ENSURE_ARG(aControlChannel); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); - - RefPtr<PresentationTerminateRequest> request = - new PresentationTerminateRequest(aDevice, aPresentationId, - aControlChannel, aIsFromReceiver); - obs->NotifyObservers(request, - PRESENTATION_TERMINATE_REQUEST_TOPIC, - nullptr); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceManager::OnReconnectRequest(nsIPresentationDevice* aDevice, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - NS_ENSURE_ARG(aDevice); - NS_ENSURE_ARG(aControlChannel); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); - - RefPtr<PresentationSessionRequest> request = - new PresentationSessionRequest(aDevice, aUrl, aPresentationId, aControlChannel); - obs->NotifyObservers(request, - PRESENTATION_RECONNECT_REQUEST_TOPIC, - nullptr); - - return NS_OK; -} - -// nsIObserver -NS_IMETHODIMP -PresentationDeviceManager::Observe(nsISupports *aSubject, - const char *aTopic, - const char16_t *aData) -{ - if (!strcmp(aTopic, "profile-after-change")) { - Init(); - } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { - Shutdown(); - } - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationDeviceManager.h b/dom/presentation/PresentationDeviceManager.h deleted file mode 100644 index f854ee883..000000000 --- a/dom/presentation/PresentationDeviceManager.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- 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_PresentationDeviceManager_h__ -#define mozilla_dom_PresentationDeviceManager_h__ - -#include "nsIObserver.h" -#include "nsIPresentationDevice.h" -#include "nsIPresentationDeviceManager.h" -#include "nsIPresentationDeviceProvider.h" -#include "nsCOMArray.h" -#include "nsWeakReference.h" - -namespace mozilla { -namespace dom { - -class PresentationDeviceManager final : public nsIPresentationDeviceManager - , public nsIPresentationDeviceListener - , public nsIObserver - , public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICEMANAGER - NS_DECL_NSIPRESENTATIONDEVICELISTENER - NS_DECL_NSIOBSERVER - - PresentationDeviceManager(); - -private: - virtual ~PresentationDeviceManager(); - - void Init(); - - void Shutdown(); - - void LoadDeviceProviders(); - - void UnloadDeviceProviders(); - - void NotifyDeviceChange(nsIPresentationDevice* aDevice, - const char16_t* aType); - - nsCOMArray<nsIPresentationDeviceProvider> mProviders; - nsCOMArray<nsIPresentationDevice> mDevices; -}; - -} // namespace dom -} // namespace mozilla - -#endif /* mozilla_dom_PresentationDeviceManager_h__ */ diff --git a/dom/presentation/PresentationLog.h b/dom/presentation/PresentationLog.h deleted file mode 100644 index 96af0c124..000000000 --- a/dom/presentation/PresentationLog.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- 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_PresentationLog_h -#define mozilla_dom_PresentationLog_h - -/* - * MOZ_LOG=Presentation:5 - * For detail, see PresentationService.cpp - */ -namespace mozilla { -namespace dom { -extern mozilla::LazyLogModule gPresentationLog; -} -} - -#undef PRES_ERROR -#define PRES_ERROR(...) MOZ_LOG(mozilla::dom::gPresentationLog, mozilla::LogLevel::Error, (__VA_ARGS__)) - -#undef PRES_DEBUG -#define PRES_DEBUG(...) MOZ_LOG(mozilla::dom::gPresentationLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -#endif // mozilla_dom_PresentationLog_h diff --git a/dom/presentation/PresentationNetworkHelper.js b/dom/presentation/PresentationNetworkHelper.js deleted file mode 100644 index 9b6458daf..000000000 --- a/dom/presentation/PresentationNetworkHelper.js +++ /dev/null @@ -1,28 +0,0 @@ -// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- -/* 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/. */ - -"use strict"; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Messaging.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -const NETWORKHELPER_CID = Components.ID("{5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d}"); - -function PresentationNetworkHelper() {} - -PresentationNetworkHelper.prototype = { - classID: NETWORKHELPER_CID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationNetworkHelper]), - - getWifiIPAddress: function(aListener) { - Messaging.sendRequestForResult({type: "Wifi:GetIPAddress"}) - .then(result => aListener.onGetWifiIPAddress(result), - err => aListener.onError(err)); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationNetworkHelper]); diff --git a/dom/presentation/PresentationNetworkHelper.manifest b/dom/presentation/PresentationNetworkHelper.manifest deleted file mode 100644 index a061cef08..000000000 --- a/dom/presentation/PresentationNetworkHelper.manifest +++ /dev/null @@ -1,3 +0,0 @@ -# PresentationNetworkHelper.js -component {5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d} PresentationNetworkHelper.js -contract @mozilla.org/presentation-device/networkHelper;1 {5fb96caa-6d49-4f6b-9a4b-65dd0d51f92d} diff --git a/dom/presentation/PresentationReceiver.cpp b/dom/presentation/PresentationReceiver.cpp deleted file mode 100644 index bc1776b45..000000000 --- a/dom/presentation/PresentationReceiver.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "PresentationReceiver.h" - -#include "mozilla/dom/PresentationReceiverBinding.h" -#include "mozilla/dom/Promise.h" -#include "nsContentUtils.h" -#include "nsIPresentationService.h" -#include "nsPIDOMWindow.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "PresentationConnection.h" -#include "PresentationConnectionList.h" -#include "PresentationLog.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PresentationReceiver, - mOwner, - mGetConnectionListPromise, - mConnectionList) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationReceiver) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationReceiver) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationReceiver) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsIPresentationRespondingListener) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -/* static */ already_AddRefed<PresentationReceiver> -PresentationReceiver::Create(nsPIDOMWindowInner* aWindow) -{ - RefPtr<PresentationReceiver> receiver = new PresentationReceiver(aWindow); - return NS_WARN_IF(!receiver->Init()) ? nullptr : receiver.forget(); -} - -PresentationReceiver::PresentationReceiver(nsPIDOMWindowInner* aWindow) - : mOwner(aWindow) -{ - MOZ_ASSERT(aWindow); -} - -PresentationReceiver::~PresentationReceiver() -{ - Shutdown(); -} - -bool -PresentationReceiver::Init() -{ - if (NS_WARN_IF(!mOwner)) { - return false; - } - mWindowId = mOwner->WindowID(); - - nsCOMPtr<nsIDocShell> docShell = mOwner->GetDocShell(); - MOZ_ASSERT(docShell); - - nsContentUtils::GetPresentationURL(docShell, mUrl); - return !mUrl.IsEmpty(); -} - -void PresentationReceiver::Shutdown() -{ - PRES_DEBUG("receiver shutdown:windowId[%d]\n", mWindowId); - - // Unregister listener for incoming sessions. - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return; - } - - Unused << - NS_WARN_IF(NS_FAILED(service->UnregisterRespondingListener(mWindowId))); -} - -/* virtual */ JSObject* -PresentationReceiver::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationReceiverBinding::Wrap(aCx, this, aGivenProto); -} - -NS_IMETHODIMP -PresentationReceiver::NotifySessionConnect(uint64_t aWindowId, - const nsAString& aSessionId) -{ - PRES_DEBUG("receiver session connect:id[%s], windowId[%x]\n", - NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId); - - if (NS_WARN_IF(!mOwner)) { - return NS_ERROR_FAILURE; - } - - if (NS_WARN_IF(aWindowId != mWindowId)) { - return NS_ERROR_INVALID_ARG; - } - - if (NS_WARN_IF(!mConnectionList)) { - return NS_ERROR_FAILURE; - } - - RefPtr<PresentationConnection> connection = - PresentationConnection::Create(mOwner, aSessionId, mUrl, - nsIPresentationService::ROLE_RECEIVER, - mConnectionList); - if (NS_WARN_IF(!connection)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return NS_OK; -} - -already_AddRefed<Promise> -PresentationReceiver::GetConnectionList(ErrorResult& aRv) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mOwner); - if (NS_WARN_IF(!global)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - if (!mGetConnectionListPromise) { - mGetConnectionListPromise = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr<PresentationReceiver> self = this; - nsresult rv = - NS_DispatchToMainThread(NS_NewRunnableFunction([self] () -> void { - self->CreateConnectionList(); - })); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - } - - RefPtr<Promise> promise = mGetConnectionListPromise; - return promise.forget(); -} - -void -PresentationReceiver::CreateConnectionList() -{ - MOZ_ASSERT(mGetConnectionListPromise); - - if (mConnectionList) { - return; - } - - mConnectionList = new PresentationConnectionList(mOwner, - mGetConnectionListPromise); - - // Register listener for incoming sessions. - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - mGetConnectionListPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - nsresult rv = service->RegisterRespondingListener(mWindowId, this); - if (NS_WARN_IF(NS_FAILED(rv))) { - mGetConnectionListPromise->MaybeReject(rv); - } -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationReceiver.h b/dom/presentation/PresentationReceiver.h deleted file mode 100644 index ee72f587b..000000000 --- a/dom/presentation/PresentationReceiver.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationReceiver_h -#define mozilla_dom_PresentationReceiver_h - -#include "mozilla/ErrorResult.h" -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIPresentationListener.h" -#include "nsWrapperCache.h" -#include "nsString.h" - -class nsPIDOMWindowInner; - -namespace mozilla { -namespace dom { - -class PresentationConnection; -class PresentationConnectionList; -class Promise; - -class PresentationReceiver final : public nsIPresentationRespondingListener - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PresentationReceiver) - NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER - - static already_AddRefed<PresentationReceiver> Create(nsPIDOMWindowInner* aWindow); - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - nsPIDOMWindowInner* GetParentObject() const - { - return mOwner; - } - - // WebIDL (public APIs) - already_AddRefed<Promise> GetConnectionList(ErrorResult& aRv); - -private: - explicit PresentationReceiver(nsPIDOMWindowInner* aWindow); - - virtual ~PresentationReceiver(); - - MOZ_IS_CLASS_INIT bool Init(); - - void Shutdown(); - - void CreateConnectionList(); - - // Store the inner window ID for |UnregisterRespondingListener| call in - // |Shutdown| since the inner window may not exist at that moment. - uint64_t mWindowId; - - nsCOMPtr<nsPIDOMWindowInner> mOwner; - nsString mUrl; - RefPtr<Promise> mGetConnectionListPromise; - RefPtr<PresentationConnectionList> mConnectionList; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationReceiver_h diff --git a/dom/presentation/PresentationRequest.cpp b/dom/presentation/PresentationRequest.cpp deleted file mode 100644 index 221684e53..000000000 --- a/dom/presentation/PresentationRequest.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "PresentationRequest.h" - -#include "AvailabilityCollection.h" -#include "ControllerConnectionCollection.h" -#include "mozilla/BasePrincipal.h" -#include "mozilla/dom/Navigator.h" -#include "mozilla/dom/PresentationRequestBinding.h" -#include "mozilla/dom/PresentationConnectionAvailableEvent.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/Move.h" -#include "mozIThirdPartyUtil.h" -#include "nsContentSecurityManager.h" -#include "nsCycleCollectionParticipant.h" -#include "nsGlobalWindow.h" -#include "nsIDocument.h" -#include "nsIPresentationService.h" -#include "nsIURI.h" -#include "nsIUUIDGenerator.h" -#include "nsNetUtil.h" -#include "nsSandboxFlags.h" -#include "nsServiceManagerUtils.h" -#include "Presentation.h" -#include "PresentationAvailability.h" -#include "PresentationCallbacks.h" -#include "PresentationLog.h" -#include "PresentationTransportBuilderConstructor.h" - -using namespace mozilla; -using namespace mozilla::dom; - -NS_IMPL_ADDREF_INHERITED(PresentationRequest, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(PresentationRequest, DOMEventTargetHelper) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PresentationRequest) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -static nsresult -GetAbsoluteURL(const nsAString& aUrl, - nsIURI* aBaseUri, - nsIDocument* aDocument, - nsAString& aAbsoluteUrl) -{ - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), - aUrl, - aDocument ? aDocument->GetDocumentCharacterSet().get() - : nullptr, - aBaseUri); - - if (NS_FAILED(rv)) { - return rv; - } - - nsAutoCString spec; - uri->GetSpec(spec); - - aAbsoluteUrl = NS_ConvertUTF8toUTF16(spec); - - return NS_OK; -} - -/* static */ already_AddRefed<PresentationRequest> -PresentationRequest::Constructor(const GlobalObject& aGlobal, - const nsAString& aUrl, - ErrorResult& aRv) -{ - Sequence<nsString> urls; - urls.AppendElement(aUrl, fallible); - return Constructor(aGlobal, urls, aRv); -} - -/* static */ already_AddRefed<PresentationRequest> -PresentationRequest::Constructor(const GlobalObject& aGlobal, - const Sequence<nsString>& aUrls, - ErrorResult& aRv) -{ - nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); - if (!window) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - if (aUrls.IsEmpty()) { - aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return nullptr; - } - - // Resolve relative URL to absolute URL - nsCOMPtr<nsIURI> baseUri = window->GetDocBaseURI(); - nsTArray<nsString> urls; - for (const auto& url : aUrls) { - nsAutoString absoluteUrl; - nsresult rv = - GetAbsoluteURL(url, baseUri, window->GetExtantDoc(), absoluteUrl); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return nullptr; - } - - urls.AppendElement(absoluteUrl); - } - - RefPtr<PresentationRequest> request = - new PresentationRequest(window, Move(urls)); - return NS_WARN_IF(!request->Init()) ? nullptr : request.forget(); -} - -PresentationRequest::PresentationRequest(nsPIDOMWindowInner* aWindow, - nsTArray<nsString>&& aUrls) - : DOMEventTargetHelper(aWindow) - , mUrls(Move(aUrls)) -{ -} - -PresentationRequest::~PresentationRequest() -{ -} - -bool -PresentationRequest::Init() -{ - return true; -} - -/* virtual */ JSObject* -PresentationRequest::WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) -{ - return PresentationRequestBinding::Wrap(aCx, this, aGivenProto); -} - -already_AddRefed<Promise> -PresentationRequest::Start(ErrorResult& aRv) -{ - return StartWithDevice(NullString(), aRv); -} - -already_AddRefed<Promise> -PresentationRequest::StartWithDevice(const nsAString& aDeviceId, - ErrorResult& aRv) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - if (NS_WARN_IF(!global)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - // Get the origin. - nsAutoString origin; - nsresult rv = nsContentUtils::GetUTFOrigin(global->PrincipalOrNull(), origin); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); - return nullptr; - } - - nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc(); - if (NS_WARN_IF(!doc)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<Promise> promise = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - if (IsProhibitMixedSecurityContexts(doc) && - !IsAllURLAuthenticated()) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - RefPtr<Navigator> navigator = - nsGlobalWindow::Cast(GetOwner())->GetNavigator(aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr<Presentation> presentation = navigator->GetPresentation(aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - if (presentation->IsStartSessionUnsettled()) { - promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return promise.forget(); - } - - // Generate a session ID. - nsCOMPtr<nsIUUIDGenerator> uuidgen = - do_GetService("@mozilla.org/uuid-generator;1"); - if(NS_WARN_IF(!uuidgen)) { - promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return promise.forget(); - } - - nsID uuid; - uuidgen->GenerateUUIDInPlace(&uuid); - char buffer[NSID_LENGTH]; - uuid.ToProvidedString(buffer); - nsAutoString id; - CopyASCIItoUTF16(buffer, id); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return promise.forget(); - } - - presentation->SetStartSessionUnsettled(true); - - // Get xul:browser element in parent process or nsWindowRoot object in child - // process. If it's in child process, the corresponding xul:browser element - // will be obtained at PresentationRequestParent::DoRequest in its parent - // process. - nsCOMPtr<nsIDOMEventTarget> handler = - do_QueryInterface(GetOwner()->GetChromeEventHandler()); - nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal(); - nsCOMPtr<nsIPresentationServiceCallback> callback = - new PresentationRequesterCallback(this, id, promise); - nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = - PresentationTransportBuilderConstructor::Create(); - rv = service->StartSession(mUrls, - id, - origin, - aDeviceId, - GetOwner()->WindowID(), - handler, - principal, - callback, - constructor); - if (NS_WARN_IF(NS_FAILED(rv))) { - promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - NotifyPromiseSettled(); - } - - return promise.forget(); -} - -already_AddRefed<Promise> -PresentationRequest::Reconnect(const nsAString& aPresentationId, - ErrorResult& aRv) -{ - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - if (NS_WARN_IF(!global)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc(); - if (NS_WARN_IF(!doc)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<Promise> promise = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - if (IsProhibitMixedSecurityContexts(doc) && - !IsAllURLAuthenticated()) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - nsString presentationId = nsString(aPresentationId); - nsCOMPtr<nsIRunnable> r = - NewRunnableMethod<nsString, RefPtr<Promise>>( - this, - &PresentationRequest::FindOrCreatePresentationConnection, - presentationId, - promise); - - if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) { - promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - } - - return promise.forget(); -} - -void -PresentationRequest::FindOrCreatePresentationConnection( - const nsAString& aPresentationId, - Promise* aPromise) -{ - MOZ_ASSERT(aPromise); - - if (NS_WARN_IF(!GetOwner())) { - aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - RefPtr<PresentationConnection> connection = - ControllerConnectionCollection::GetSingleton()->FindConnection( - GetOwner()->WindowID(), - aPresentationId, - nsIPresentationService::ROLE_CONTROLLER); - - if (connection) { - nsAutoString url; - connection->GetUrl(url); - if (mUrls.Contains(url)) { - switch (connection->State()) { - case PresentationConnectionState::Closed: - // We found the matched connection. - break; - case PresentationConnectionState::Connecting: - case PresentationConnectionState::Connected: - aPromise->MaybeResolve(connection); - return; - case PresentationConnectionState::Terminated: - // A terminated connection cannot be reused. - connection = nullptr; - break; - default: - MOZ_CRASH("Unknown presentation session state."); - return; - } - } else { - connection = nullptr; - } - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if(NS_WARN_IF(!service)) { - aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - nsCOMPtr<nsIPresentationServiceCallback> callback = - new PresentationReconnectCallback(this, - aPresentationId, - aPromise, - connection); - - nsresult rv = - service->ReconnectSession(mUrls, - aPresentationId, - nsIPresentationService::ROLE_CONTROLLER, - callback); - if (NS_WARN_IF(NS_FAILED(rv))) { - aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - } -} - -already_AddRefed<Promise> -PresentationRequest::GetAvailability(ErrorResult& aRv) -{ - PRES_DEBUG("%s\n", __func__); - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); - if (NS_WARN_IF(!global)) { - aRv.Throw(NS_ERROR_UNEXPECTED); - return nullptr; - } - - nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc(); - if (NS_WARN_IF(!doc)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - RefPtr<Promise> promise = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - if (IsProhibitMixedSecurityContexts(doc) && - !IsAllURLAuthenticated()) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - if (doc->GetSandboxFlags() & SANDBOXED_PRESENTATION) { - promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); - return promise.forget(); - } - - FindOrCreatePresentationAvailability(promise); - - return promise.forget(); -} - -void -PresentationRequest::FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise) -{ - MOZ_ASSERT(aPromise); - - if (NS_WARN_IF(!GetOwner())) { - aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - AvailabilityCollection* collection = AvailabilityCollection::GetSingleton(); - if (NS_WARN_IF(!collection)) { - aPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - RefPtr<PresentationAvailability> availability = - collection->Find(GetOwner()->WindowID(), mUrls); - - if (!availability) { - availability = PresentationAvailability::Create(GetOwner(), mUrls, aPromise); - } else { - PRES_DEBUG(">resolve with same object\n"); - - // Fetching cached available devices is asynchronous in our implementation, - // we need to ensure the promise is resolved in order. - if (availability->IsCachedValueReady()) { - aPromise->MaybeResolve(availability); - return; - } - - availability->EnqueuePromise(aPromise); - } - - if (!availability) { - aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } -} - -nsresult -PresentationRequest::DispatchConnectionAvailableEvent(PresentationConnection* aConnection) -{ - PresentationConnectionAvailableEventInit init; - init.mConnection = aConnection; - - RefPtr<PresentationConnectionAvailableEvent> event = - PresentationConnectionAvailableEvent::Constructor(this, - NS_LITERAL_STRING("connectionavailable"), - init); - if (NS_WARN_IF(!event)) { - return NS_ERROR_FAILURE; - } - event->SetTrusted(true); - - RefPtr<AsyncEventDispatcher> asyncDispatcher = - new AsyncEventDispatcher(this, event); - return asyncDispatcher->PostDOMEvent(); -} - -void -PresentationRequest::NotifyPromiseSettled() -{ - PRES_DEBUG("%s\n", __func__); - - if (!GetOwner()) { - return; - } - - ErrorResult rv; - RefPtr<Navigator> navigator = - nsGlobalWindow::Cast(GetOwner())->GetNavigator(rv); - if (!navigator) { - return; - } - - RefPtr<Presentation> presentation = navigator->GetPresentation(rv); - - if (presentation) { - presentation->SetStartSessionUnsettled(false); - } -} - -bool -PresentationRequest::IsProhibitMixedSecurityContexts(nsIDocument* aDocument) -{ - MOZ_ASSERT(aDocument); - - if (nsContentUtils::IsChromeDoc(aDocument)) { - return true; - } - - nsCOMPtr<nsIDocument> doc = aDocument; - while (doc && !nsContentUtils::IsChromeDoc(doc)) { - if (nsContentUtils::HttpsStateIsModern(doc)) { - return true; - } - - doc = doc->GetParentDocument(); - } - - return false; -} - -bool -PresentationRequest::IsPrioriAuthenticatedURL(const nsAString& aUrl) -{ - nsCOMPtr<nsIURI> uri; - if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aUrl))) { - return false; - } - - nsAutoCString scheme; - nsresult rv = uri->GetScheme(scheme); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - if (scheme.EqualsLiteral("data")) { - return true; - } - - nsAutoCString uriSpec; - rv = uri->GetSpec(uriSpec); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - if (uriSpec.EqualsLiteral("about:blank") || - uriSpec.EqualsLiteral("about:srcdoc")) { - return true; - } - - PrincipalOriginAttributes attrs; - nsCOMPtr<nsIPrincipal> principal = - BasePrincipal::CreateCodebasePrincipal(uri, attrs); - if (NS_WARN_IF(!principal)) { - return false; - } - - nsCOMPtr<nsIContentSecurityManager> csm = - do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID); - if (NS_WARN_IF(!csm)) { - return false; - } - - bool isTrustworthyOrigin = false; - csm->IsOriginPotentiallyTrustworthy(principal, &isTrustworthyOrigin); - return isTrustworthyOrigin; -} - -bool -PresentationRequest::IsAllURLAuthenticated() -{ - for (const auto& url : mUrls) { - if (!IsPrioriAuthenticatedURL(url)) { - return false; - } - } - - return true; -} diff --git a/dom/presentation/PresentationRequest.h b/dom/presentation/PresentationRequest.h deleted file mode 100644 index ce82f2b44..000000000 --- a/dom/presentation/PresentationRequest.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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_PresentationRequest_h -#define mozilla_dom_PresentationRequest_h - -#include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/DOMEventTargetHelper.h" - -class nsIDocument; - -namespace mozilla { -namespace dom { - -class Promise; -class PresentationAvailability; -class PresentationConnection; - -class PresentationRequest final : public DOMEventTargetHelper -{ -public: - NS_DECL_ISUPPORTS_INHERITED - - static already_AddRefed<PresentationRequest> Constructor( - const GlobalObject& aGlobal, - const nsAString& aUrl, - ErrorResult& aRv); - - static already_AddRefed<PresentationRequest> Constructor( - const GlobalObject& aGlobal, - const Sequence<nsString>& aUrls, - ErrorResult& aRv); - - virtual JSObject* WrapObject(JSContext* aCx, - JS::Handle<JSObject*> aGivenProto) override; - - // WebIDL (public APIs) - already_AddRefed<Promise> Start(ErrorResult& aRv); - - already_AddRefed<Promise> StartWithDevice(const nsAString& aDeviceId, - ErrorResult& aRv); - - already_AddRefed<Promise> Reconnect(const nsAString& aPresentationId, - ErrorResult& aRv); - - already_AddRefed<Promise> GetAvailability(ErrorResult& aRv); - - IMPL_EVENT_HANDLER(connectionavailable); - - nsresult DispatchConnectionAvailableEvent(PresentationConnection* aConnection); - - void NotifyPromiseSettled(); - -private: - PresentationRequest(nsPIDOMWindowInner* aWindow, - nsTArray<nsString>&& aUrls); - - ~PresentationRequest(); - - bool Init(); - - void FindOrCreatePresentationConnection(const nsAString& aPresentationId, - Promise* aPromise); - - void FindOrCreatePresentationAvailability(RefPtr<Promise>& aPromise); - - // Implement https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object - bool IsProhibitMixedSecurityContexts(nsIDocument* aDocument); - - // Implement https://w3c.github.io/webappsec-mixed-content/#a-priori-authenticated-url - bool IsPrioriAuthenticatedURL(const nsAString& aUrl); - - bool IsAllURLAuthenticated(); - - nsTArray<nsString> mUrls; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationRequest_h diff --git a/dom/presentation/PresentationService.cpp b/dom/presentation/PresentationService.cpp deleted file mode 100644 index bc525cdb8..000000000 --- a/dom/presentation/PresentationService.cpp +++ /dev/null @@ -1,1188 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 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 "PresentationService.h" - -#include "ipc/PresentationIPCService.h" -#include "mozilla/Services.h" -#include "nsGlobalWindow.h" -#include "nsIMutableArray.h" -#include "nsIObserverService.h" -#include "nsIPresentationControlChannel.h" -#include "nsIPresentationDeviceManager.h" -#include "nsIPresentationDevicePrompt.h" -#include "nsIPresentationListener.h" -#include "nsIPresentationRequestUIGlue.h" -#include "nsIPresentationSessionRequest.h" -#include "nsIPresentationTerminateRequest.h" -#include "nsISupportsPrimitives.h" -#include "nsNetUtil.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "nsXPCOMCID.h" -#include "nsXULAppAPI.h" -#include "PresentationLog.h" - -namespace mozilla { -namespace dom { - -static bool -IsSameDevice(nsIPresentationDevice* aDevice, nsIPresentationDevice* aDeviceAnother) { - if (!aDevice || !aDeviceAnother) { - return false; - } - - nsAutoCString deviceId; - aDevice->GetId(deviceId); - nsAutoCString anotherId; - aDeviceAnother->GetId(anotherId); - if (!deviceId.Equals(anotherId)) { - return false; - } - - nsAutoCString deviceType; - aDevice->GetType(deviceType); - nsAutoCString anotherType; - aDeviceAnother->GetType(anotherType); - if (!deviceType.Equals(anotherType)) { - return false; - } - - return true; -} - -static nsresult -ConvertURLArrayHelper(const nsTArray<nsString>& aUrls, nsIArray** aResult) -{ - if (!aResult) { - return NS_ERROR_INVALID_POINTER; - } - - *aResult = nullptr; - - nsresult rv; - nsCOMPtr<nsIMutableArray> urls = - do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (const auto& url : aUrls) { - nsCOMPtr<nsISupportsString> isupportsString = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = isupportsString->SetData(url); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = urls->AppendElement(isupportsString, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - urls.forget(aResult); - return NS_OK; -} - -/* - * Implementation of PresentationDeviceRequest - */ - -class PresentationDeviceRequest final : public nsIPresentationDeviceRequest -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICEREQUEST - - PresentationDeviceRequest( - const nsTArray<nsString>& aUrls, - const nsAString& aId, - const nsAString& aOrigin, - uint64_t aWindowId, - nsIDOMEventTarget* aEventTarget, - nsIPrincipal* aPrincipal, - nsIPresentationServiceCallback* aCallback, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor); - -private: - virtual ~PresentationDeviceRequest() = default; - nsresult CreateSessionInfo(nsIPresentationDevice* aDevice, - const nsAString& aSelectedRequestUrl); - - nsTArray<nsString> mRequestUrls; - nsString mId; - nsString mOrigin; - uint64_t mWindowId; - nsWeakPtr mChromeEventHandler; - nsCOMPtr<nsIPrincipal> mPrincipal; - nsCOMPtr<nsIPresentationServiceCallback> mCallback; - nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor; -}; - -LazyLogModule gPresentationLog("Presentation"); - -NS_IMPL_ISUPPORTS(PresentationDeviceRequest, nsIPresentationDeviceRequest) - -PresentationDeviceRequest::PresentationDeviceRequest( - const nsTArray<nsString>& aUrls, - const nsAString& aId, - const nsAString& aOrigin, - uint64_t aWindowId, - nsIDOMEventTarget* aEventTarget, - nsIPrincipal* aPrincipal, - nsIPresentationServiceCallback* aCallback, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) - : mRequestUrls(aUrls) - , mId(aId) - , mOrigin(aOrigin) - , mWindowId(aWindowId) - , mChromeEventHandler(do_GetWeakReference(aEventTarget)) - , mPrincipal(aPrincipal) - , mCallback(aCallback) - , mBuilderConstructor(aBuilderConstructor) -{ - MOZ_ASSERT(!mRequestUrls.IsEmpty()); - MOZ_ASSERT(!mId.IsEmpty()); - MOZ_ASSERT(!mOrigin.IsEmpty()); - MOZ_ASSERT(mCallback); - MOZ_ASSERT(mBuilderConstructor); -} - -NS_IMETHODIMP -PresentationDeviceRequest::GetOrigin(nsAString& aOrigin) -{ - aOrigin = mOrigin; - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceRequest::GetRequestURLs(nsIArray** aUrls) -{ - return ConvertURLArrayHelper(mRequestUrls, aUrls); -} - -NS_IMETHODIMP -PresentationDeviceRequest::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler) -{ - nsCOMPtr<nsIDOMEventTarget> handler(do_QueryReferent(mChromeEventHandler)); - handler.forget(aChromeEventHandler); - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceRequest::GetPrincipal(nsIPrincipal** aPrincipal) -{ - nsCOMPtr<nsIPrincipal> principal(mPrincipal); - principal.forget(aPrincipal); - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceRequest::Select(nsIPresentationDevice* aDevice) -{ - MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!aDevice)) { - MOZ_ASSERT(false, "|aDevice| should noe be null."); - mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - return NS_ERROR_INVALID_ARG; - } - - // Select the most suitable URL for starting the presentation. - nsAutoString selectedRequestUrl; - for (const auto& url : mRequestUrls) { - bool isSupported; - if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) && - isSupported) { - selectedRequestUrl.Assign(url); - break; - } - } - - if (selectedRequestUrl.IsEmpty()) { - return mCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR); - } - - if (NS_WARN_IF(NS_FAILED(CreateSessionInfo(aDevice, selectedRequestUrl)))) { - return mCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - return mCallback->NotifySuccess(selectedRequestUrl); -} - -nsresult -PresentationDeviceRequest::CreateSessionInfo( - nsIPresentationDevice* aDevice, - const nsAString& aSelectedRequestUrl) -{ - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - // Create the controlling session info - RefPtr<PresentationSessionInfo> info = - static_cast<PresentationService*>(service.get())-> - CreateControllingSessionInfo(aSelectedRequestUrl, mId, mWindowId); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - info->SetDevice(aDevice); - - // Establish a control channel. If we failed to do so, the callback is called - // with an error message. - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - nsresult rv = aDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - // Initialize the session info with the control channel. - rv = info->Init(ctrlChannel); - if (NS_WARN_IF(NS_FAILED(rv))) { - return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - info->SetTransportBuilderConstructor(mBuilderConstructor); - return NS_OK; -} - -NS_IMETHODIMP -PresentationDeviceRequest::Cancel(nsresult aReason) -{ - return mCallback->NotifyError(aReason); -} - -/* - * Implementation of PresentationService - */ - -NS_IMPL_ISUPPORTS(PresentationService, - nsIPresentationService, - nsIObserver) - -PresentationService::PresentationService() -{ -} - -PresentationService::~PresentationService() -{ - HandleShutdown(); -} - -bool -PresentationService::Init() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (NS_WARN_IF(!obs)) { - return false; - } - - nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - rv = obs->AddObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - rv = obs->AddObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - rv = obs->AddObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - rv = obs->AddObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - return !NS_WARN_IF(NS_FAILED(rv)); -} - -NS_IMETHODIMP -PresentationService::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { - HandleShutdown(); - return NS_OK; - } else if (!strcmp(aTopic, PRESENTATION_DEVICE_CHANGE_TOPIC)) { - // Ignore the "update" case here, since we only care about the arrival and - // removal of the device. - if (!NS_strcmp(aData, u"add")) { - nsCOMPtr<nsIPresentationDevice> device = do_QueryInterface(aSubject); - if (NS_WARN_IF(!device)) { - return NS_ERROR_FAILURE; - } - - return HandleDeviceAdded(device); - } else if(!NS_strcmp(aData, u"remove")) { - return HandleDeviceRemoved(); - } - - return NS_OK; - } else if (!strcmp(aTopic, PRESENTATION_SESSION_REQUEST_TOPIC)) { - nsCOMPtr<nsIPresentationSessionRequest> request(do_QueryInterface(aSubject)); - if (NS_WARN_IF(!request)) { - return NS_ERROR_FAILURE; - } - - return HandleSessionRequest(request); - } else if (!strcmp(aTopic, PRESENTATION_TERMINATE_REQUEST_TOPIC)) { - nsCOMPtr<nsIPresentationTerminateRequest> request(do_QueryInterface(aSubject)); - if (NS_WARN_IF(!request)) { - return NS_ERROR_FAILURE; - } - - return HandleTerminateRequest(request); - } else if (!strcmp(aTopic, PRESENTATION_RECONNECT_REQUEST_TOPIC)) { - nsCOMPtr<nsIPresentationSessionRequest> request(do_QueryInterface(aSubject)); - if (NS_WARN_IF(!request)) { - return NS_ERROR_FAILURE; - } - - return HandleReconnectRequest(request); - } else if (!strcmp(aTopic, "profile-after-change")) { - // It's expected since we add and entry to |kLayoutCategories| in - // |nsLayoutModule.cpp| to launch this service earlier. - return NS_OK; - } - - MOZ_ASSERT(false, "Unexpected topic for PresentationService"); - return NS_ERROR_UNEXPECTED; -} - -void -PresentationService::HandleShutdown() -{ - MOZ_ASSERT(NS_IsMainThread()); - - Shutdown(); - - mAvailabilityManager.Clear(); - mSessionInfoAtController.Clear(); - mSessionInfoAtReceiver.Clear(); - - nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - obs->RemoveObserver(this, PRESENTATION_DEVICE_CHANGE_TOPIC); - obs->RemoveObserver(this, PRESENTATION_SESSION_REQUEST_TOPIC); - obs->RemoveObserver(this, PRESENTATION_TERMINATE_REQUEST_TOPIC); - obs->RemoveObserver(this, PRESENTATION_RECONNECT_REQUEST_TOPIC); - } -} - -nsresult -PresentationService::HandleDeviceAdded(nsIPresentationDevice* aDevice) -{ - PRES_DEBUG("%s\n", __func__); - if (!aDevice) { - MOZ_ASSERT(false, "aDevice shoud no be null."); - return NS_ERROR_INVALID_ARG; - } - - // Query for only unavailable URLs while device added. - nsTArray<nsString> unavailableUrls; - mAvailabilityManager.GetAvailbilityUrlByAvailability(unavailableUrls, false); - - nsTArray<nsString> supportedAvailabilityUrl; - for (const auto& url : unavailableUrls) { - bool isSupported; - if (NS_SUCCEEDED(aDevice->IsRequestedUrlSupported(url, &isSupported)) && - isSupported) { - supportedAvailabilityUrl.AppendElement(url); - } - } - - if (!supportedAvailabilityUrl.IsEmpty()) { - return mAvailabilityManager.DoNotifyAvailableChange(supportedAvailabilityUrl, - true); - } - - return NS_OK; -} - -nsresult -PresentationService::HandleDeviceRemoved() -{ - PRES_DEBUG("%s\n", __func__); - - // Query for only available URLs while device removed. - nsTArray<nsString> availabilityUrls; - mAvailabilityManager.GetAvailbilityUrlByAvailability(availabilityUrls, true); - - return UpdateAvailabilityUrlChange(availabilityUrls); -} - -nsresult -PresentationService::UpdateAvailabilityUrlChange( - const nsTArray<nsString>& aAvailabilityUrls) -{ - nsCOMPtr<nsIPresentationDeviceManager> deviceManager = - do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID); - if (NS_WARN_IF(!deviceManager)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIArray> devices; - nsresult rv = deviceManager->GetAvailableDevices(nullptr, - getter_AddRefs(devices)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - uint32_t numOfDevices; - devices->GetLength(&numOfDevices); - - nsTArray<nsString> supportedAvailabilityUrl; - for (const auto& url : aAvailabilityUrls) { - for (uint32_t i = 0; i < numOfDevices; ++i) { - nsCOMPtr<nsIPresentationDevice> device = do_QueryElementAt(devices, i); - if (device) { - bool isSupported; - if (NS_SUCCEEDED(device->IsRequestedUrlSupported(url, &isSupported)) && - isSupported) { - supportedAvailabilityUrl.AppendElement(url); - break; - } - } - } - } - - if (supportedAvailabilityUrl.IsEmpty()) { - return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls, - false); - } - - return mAvailabilityManager.DoNotifyAvailableChange(supportedAvailabilityUrl, - true); -} - -nsresult -PresentationService::HandleSessionRequest(nsIPresentationSessionRequest* aRequest) -{ - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) { - return rv; - } - - nsAutoString url; - rv = aRequest->GetUrl(url); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - nsAutoString sessionId; - rv = aRequest->GetPresentationId(sessionId); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - nsCOMPtr<nsIPresentationDevice> device; - rv = aRequest->GetDevice(getter_AddRefs(device)); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - // Create or reuse session info. - RefPtr<PresentationSessionInfo> info = - GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER); - - // This is the case for reconnecting a session. - // Update the control channel and device of the session info. - // Call |NotifyResponderReady| to indicate the receiver page is already there. - if (info) { - PRES_DEBUG("handle reconnection:id[%s]\n", - NS_ConvertUTF16toUTF8(sessionId).get()); - - info->SetControlChannel(ctrlChannel); - info->SetDevice(device); - return static_cast<PresentationPresentingInfo*>( - info.get())->DoReconnect(); - } - - // This is the case for a new session. - PRES_DEBUG("handle new session:url[%d], id[%s]\n", - NS_ConvertUTF16toUTF8(url).get(), - NS_ConvertUTF16toUTF8(sessionId).get()); - - info = new PresentationPresentingInfo(url, sessionId, device); - rv = info->Init(ctrlChannel); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - mSessionInfoAtReceiver.Put(sessionId, info); - - // Notify the receiver to launch. - nsCOMPtr<nsIPresentationRequestUIGlue> glue = - do_CreateInstance(PRESENTATION_REQUEST_UI_GLUE_CONTRACTID); - if (NS_WARN_IF(!glue)) { - ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR); - return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - nsCOMPtr<nsISupports> promise; - rv = glue->SendRequest(url, sessionId, device, getter_AddRefs(promise)); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - nsCOMPtr<Promise> realPromise = do_QueryInterface(promise); - static_cast<PresentationPresentingInfo*>(info.get())->SetPromise(realPromise); - - return NS_OK; -} - -nsresult -PresentationService::HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest) -{ - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) { - return rv; - } - - nsAutoString sessionId; - rv = aRequest->GetPresentationId(sessionId); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - nsCOMPtr<nsIPresentationDevice> device; - rv = aRequest->GetDevice(getter_AddRefs(device)); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - bool isFromReceiver; - rv = aRequest->GetIsFromReceiver(&isFromReceiver); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - RefPtr<PresentationSessionInfo> info; - if (!isFromReceiver) { - info = GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER); - } else { - info = GetSessionInfo(sessionId, nsIPresentationService::ROLE_CONTROLLER); - } - if (NS_WARN_IF(!info)) { - // Cannot terminate non-existed session. - ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR); - return NS_ERROR_DOM_ABORT_ERR; - } - - // Check if terminate request comes from known device. - RefPtr<nsIPresentationDevice> knownDevice = info->GetDevice(); - if (NS_WARN_IF(!IsSameDevice(device, knownDevice))) { - ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR); - return NS_ERROR_DOM_ABORT_ERR; - } - - PRES_DEBUG("handle termination:id[%s], receiver[%d]\n", __func__, - sessionId.get(), isFromReceiver); - - return info->OnTerminate(ctrlChannel); -} - -nsresult -PresentationService::HandleReconnectRequest(nsIPresentationSessionRequest* aRequest) -{ - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - nsresult rv = aRequest->GetControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_WARN_IF(NS_FAILED(rv) || !ctrlChannel)) { - return rv; - } - - nsAutoString sessionId; - rv = aRequest->GetPresentationId(sessionId); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - uint64_t windowId; - rv = GetWindowIdBySessionIdInternal(sessionId, - nsIPresentationService::ROLE_RECEIVER, - &windowId); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - RefPtr<PresentationSessionInfo> info = - GetSessionInfo(sessionId, nsIPresentationService::ROLE_RECEIVER); - if (NS_WARN_IF(!info)) { - // Cannot reconnect non-existed session - ctrlChannel->Disconnect(NS_ERROR_DOM_OPERATION_ERR); - return NS_ERROR_DOM_ABORT_ERR; - } - - nsAutoString url; - rv = aRequest->GetUrl(url); - if (NS_WARN_IF(NS_FAILED(rv))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - // Make sure the url is the same as the previous one. - if (NS_WARN_IF(!info->GetUrl().Equals(url))) { - ctrlChannel->Disconnect(rv); - return rv; - } - - return HandleSessionRequest(aRequest); -} - -NS_IMETHODIMP -PresentationService::StartSession( - const nsTArray<nsString>& aUrls, - const nsAString& aSessionId, - const nsAString& aOrigin, - const nsAString& aDeviceId, - uint64_t aWindowId, - nsIDOMEventTarget* aEventTarget, - nsIPrincipal* aPrincipal, - nsIPresentationServiceCallback* aCallback, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) -{ - PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get()); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(!aUrls.IsEmpty()); - - nsCOMPtr<nsIPresentationDeviceRequest> request = - new PresentationDeviceRequest(aUrls, - aSessionId, - aOrigin, - aWindowId, - aEventTarget, - aPrincipal, - aCallback, - aBuilderConstructor); - - if (aDeviceId.IsVoid()) { - // Pop up a prompt and ask user to select a device. - nsCOMPtr<nsIPresentationDevicePrompt> prompt = - do_GetService(PRESENTATION_DEVICE_PROMPT_CONTRACTID); - if (NS_WARN_IF(!prompt)) { - return aCallback->NotifyError(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - - nsresult rv = prompt->PromptDeviceSelection(request); - if (NS_WARN_IF(NS_FAILED(rv))) { - return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - return NS_OK; - } - - // Find the designated device from available device list. - nsCOMPtr<nsIPresentationDeviceManager> deviceManager = - do_GetService(PRESENTATION_DEVICE_MANAGER_CONTRACTID); - if (NS_WARN_IF(!deviceManager)) { - return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - nsCOMPtr<nsIArray> presentationUrls; - if (NS_WARN_IF(NS_FAILED( - ConvertURLArrayHelper(aUrls, getter_AddRefs(presentationUrls))))) { - return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - nsCOMPtr<nsIArray> devices; - nsresult rv = deviceManager->GetAvailableDevices(presentationUrls, getter_AddRefs(devices)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - nsCOMPtr<nsISimpleEnumerator> enumerator; - rv = devices->Enumerate(getter_AddRefs(enumerator)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return aCallback->NotifyError(NS_ERROR_DOM_OPERATION_ERR); - } - - NS_ConvertUTF16toUTF8 utf8DeviceId(aDeviceId); - bool hasMore; - while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr<nsISupports> isupports; - rv = enumerator->GetNext(getter_AddRefs(isupports)); - - nsCOMPtr<nsIPresentationDevice> device(do_QueryInterface(isupports)); - MOZ_ASSERT(device); - - nsAutoCString id; - if (NS_SUCCEEDED(device->GetId(id)) && id.Equals(utf8DeviceId)) { - request->Select(device); - return NS_OK; - } - } - - // Reject if designated device is not available. - return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR); -} - -already_AddRefed<PresentationSessionInfo> -PresentationService::CreateControllingSessionInfo(const nsAString& aUrl, - const nsAString& aSessionId, - uint64_t aWindowId) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (aSessionId.IsEmpty()) { - return nullptr; - } - - RefPtr<PresentationSessionInfo> info = - new PresentationControllingInfo(aUrl, aSessionId); - - mSessionInfoAtController.Put(aSessionId, info); - AddRespondingSessionId(aWindowId, - aSessionId, - nsIPresentationService::ROLE_CONTROLLER); - return info.forget(); -} - -NS_IMETHODIMP -PresentationService::SendSessionMessage(const nsAString& aSessionId, - uint8_t aRole, - const nsAString& aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aData.IsEmpty()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->Send(aData); -} - -NS_IMETHODIMP -PresentationService::SendSessionBinaryMsg(const nsAString& aSessionId, - uint8_t aRole, - const nsACString &aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aData.IsEmpty()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->SendBinaryMsg(aData); -} - -NS_IMETHODIMP -PresentationService::SendSessionBlob(const nsAString& aSessionId, - uint8_t aRole, - nsIDOMBlob* aBlob) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - MOZ_ASSERT(aBlob); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->SendBlob(aBlob); -} - -NS_IMETHODIMP -PresentationService::CloseSession(const nsAString& aSessionId, - uint8_t aRole, - uint8_t aClosedReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aClosedReason, aRole); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (aClosedReason == nsIPresentationService::CLOSED_REASON_WENTAWAY) { - // Remove nsIPresentationSessionListener since we don't want to dispatch - // PresentationConnectionCloseEvent if the page is went away. - info->SetListener(nullptr); - } - - return info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED); -} - -NS_IMETHODIMP -PresentationService::TerminateSession(const nsAString& aSessionId, - uint8_t aRole) -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->Close(NS_OK, nsIPresentationSessionListener::STATE_TERMINATED); -} - -NS_IMETHODIMP -PresentationService::ReconnectSession(const nsTArray<nsString>& aUrls, - const nsAString& aSessionId, - uint8_t aRole, - nsIPresentationServiceCallback* aCallback) -{ - PRES_DEBUG("%s:id[%s]\n", __func__, NS_ConvertUTF16toUTF8(aSessionId).get()); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!aUrls.IsEmpty()); - - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "Only controller can call ReconnectSession."); - return NS_ERROR_INVALID_ARG; - } - - if (NS_WARN_IF(!aCallback)) { - return NS_ERROR_INVALID_ARG; - } - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR); - } - - if (NS_WARN_IF(!aUrls.Contains(info->GetUrl()))) { - return aCallback->NotifyError(NS_ERROR_DOM_NOT_FOUND_ERR); - } - - return static_cast<PresentationControllingInfo*>(info.get())->Reconnect(aCallback); -} - -NS_IMETHODIMP -PresentationService::BuildTransport(const nsAString& aSessionId, - uint8_t aRole) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "Only controller can call BuildTransport."); - return NS_ERROR_INVALID_ARG; - } - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return static_cast<PresentationControllingInfo*>(info.get())->BuildTransport(); -} - -NS_IMETHODIMP -PresentationService::RegisterAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aAvailabilityUrls.IsEmpty()); - MOZ_ASSERT(aListener); - - mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls, aListener); - return UpdateAvailabilityUrlChange(aAvailabilityUrls); -} - -NS_IMETHODIMP -PresentationService::UnregisterAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls, aListener); - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::RegisterSessionListener(const nsAString& aSessionId, - uint8_t aRole, - nsIPresentationSessionListener* aListener) -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aListener); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - // Notify the listener of TERMINATED since no correspondent session info is - // available possibly due to establishment failure. This would be useful at - // the receiver side, since a presentation session is created at beginning - // and here is the place to realize the underlying establishment fails. - nsresult rv = aListener->NotifyStateChange(aSessionId, - nsIPresentationSessionListener::STATE_TERMINATED, - NS_ERROR_NOT_AVAILABLE); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_ERROR_NOT_AVAILABLE; - } - - return info->SetListener(aListener); -} - -NS_IMETHODIMP -PresentationService::UnregisterSessionListener(const nsAString& aSessionId, - uint8_t aRole) -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (info) { - // When content side decide not handling this session anymore, simply - // close the connection. Session info is kept for reconnection. - Unused << NS_WARN_IF(NS_FAILED(info->Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED))); - return info->SetListener(nullptr); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::RegisterRespondingListener( - uint64_t aWindowId, - nsIPresentationRespondingListener* aListener) -{ - PRES_DEBUG("%s:windowId[%lld]\n", __func__, aWindowId); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aListener); - - nsCOMPtr<nsIPresentationRespondingListener> listener; - if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) { - return (listener == aListener) ? NS_OK : NS_ERROR_DOM_INVALID_STATE_ERR; - } - - nsTArray<nsString> sessionIdArray; - nsresult rv = mReceiverSessionIdManager.GetSessionIds(aWindowId, - sessionIdArray); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (const auto& id : sessionIdArray) { - aListener->NotifySessionConnect(aWindowId, id); - } - - mRespondingListeners.Put(aWindowId, aListener); - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::UnregisterRespondingListener(uint64_t aWindowId) -{ - PRES_DEBUG("%s:windowId[%lld]\n", __func__, aWindowId); - - MOZ_ASSERT(NS_IsMainThread()); - - mRespondingListeners.Remove(aWindowId); - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::NotifyReceiverReady( - const nsAString& aSessionId, - uint64_t aWindowId, - bool aIsLoading, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) -{ - PRES_DEBUG("%s:id[%s], windowId[%lld], loading[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aWindowId, aIsLoading); - - RefPtr<PresentationSessionInfo> info = - GetSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - AddRespondingSessionId(aWindowId, - aSessionId, - nsIPresentationService::ROLE_RECEIVER); - - if (!aIsLoading) { - return static_cast<PresentationPresentingInfo*>( - info.get())->NotifyResponderFailure(); - } - - nsCOMPtr<nsIPresentationRespondingListener> listener; - if (mRespondingListeners.Get(aWindowId, getter_AddRefs(listener))) { - nsresult rv = listener->NotifySessionConnect(aWindowId, aSessionId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - info->SetTransportBuilderConstructor(aBuilderConstructor); - return static_cast<PresentationPresentingInfo*>(info.get())->NotifyResponderReady(); -} - -nsresult -PresentationService::NotifyTransportClosed(const nsAString& aSessionId, - uint8_t aRole, - nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aReason, aRole); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->NotifyTransportClosed(aReason); -} - -NS_IMETHODIMP -PresentationService::UntrackSessionInfo(const nsAString& aSessionId, - uint8_t aRole) -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); - - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - // Remove the session info. - if (nsIPresentationService::ROLE_CONTROLLER == aRole) { - mSessionInfoAtController.Remove(aSessionId); - } else { - // Terminate receiver page. - uint64_t windowId; - nsresult rv = GetWindowIdBySessionIdInternal(aSessionId, aRole, &windowId); - if (NS_SUCCEEDED(rv)) { - NS_DispatchToMainThread(NS_NewRunnableFunction([windowId]() -> void { - PRES_DEBUG("Attempt to close window[%d]\n", windowId); - - if (auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId)) { - window->Close(); - } - })); - } - - mSessionInfoAtReceiver.Remove(aSessionId); - } - - // Remove the in-process responding info if there's still any. - RemoveRespondingSessionId(aSessionId, aRole); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationService::GetWindowIdBySessionId(const nsAString& aSessionId, - uint8_t aRole, - uint64_t* aWindowId) -{ - return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); -} - -NS_IMETHODIMP -PresentationService::UpdateWindowIdBySessionId(const nsAString& aSessionId, - uint8_t aRole, - const uint64_t aWindowId) -{ - return UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); -} - -bool -PresentationService::IsSessionAccessible(const nsAString& aSessionId, - const uint8_t aRole, - base::ProcessId aProcessId) -{ - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - RefPtr<PresentationSessionInfo> info = GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return false; - } - return info->IsAccessible(aProcessId); -} - -} // namespace dom -} // namespace mozilla - -already_AddRefed<nsIPresentationService> -NS_CreatePresentationService() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr<nsIPresentationService> service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { - service = new mozilla::dom::PresentationIPCService(); - } else { - service = new PresentationService(); - if (NS_WARN_IF(!static_cast<PresentationService*>(service.get())->Init())) { - return nullptr; - } - } - - return service.forget(); -} diff --git a/dom/presentation/PresentationService.h b/dom/presentation/PresentationService.h deleted file mode 100644 index b2d39e691..000000000 --- a/dom/presentation/PresentationService.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- 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_PresentationService_h -#define mozilla_dom_PresentationService_h - -#include "nsCOMPtr.h" -#include "nsIObserver.h" -#include "PresentationServiceBase.h" -#include "PresentationSessionInfo.h" - -class nsIPresentationSessionRequest; -class nsIPresentationTerminateRequest; -class nsIURI; -class nsIPresentationSessionTransportBuilder; - -namespace mozilla { -namespace dom { - -class PresentationDeviceRequest; -class PresentationRespondingInfo; - -class PresentationService final - : public nsIPresentationService - , public nsIObserver - , public PresentationServiceBase<PresentationSessionInfo> -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - NS_DECL_NSIPRESENTATIONSERVICE - - PresentationService(); - bool Init(); - - bool IsSessionAccessible(const nsAString& aSessionId, - const uint8_t aRole, - base::ProcessId aProcessId); - -private: - friend class PresentationDeviceRequest; - - virtual ~PresentationService(); - void HandleShutdown(); - nsresult HandleDeviceAdded(nsIPresentationDevice* aDevice); - nsresult HandleDeviceRemoved(); - nsresult HandleSessionRequest(nsIPresentationSessionRequest* aRequest); - nsresult HandleTerminateRequest(nsIPresentationTerminateRequest* aRequest); - nsresult HandleReconnectRequest(nsIPresentationSessionRequest* aRequest); - - // This is meant to be called by PresentationDeviceRequest. - already_AddRefed<PresentationSessionInfo> - CreateControllingSessionInfo(const nsAString& aUrl, - const nsAString& aSessionId, - uint64_t aWindowId); - - // Emumerate all devices to get the availability of each input Urls. - nsresult UpdateAvailabilityUrlChange( - const nsTArray<nsString>& aAvailabilityUrls); -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationService_h diff --git a/dom/presentation/PresentationServiceBase.h b/dom/presentation/PresentationServiceBase.h deleted file mode 100644 index 227e95430..000000000 --- a/dom/presentation/PresentationServiceBase.h +++ /dev/null @@ -1,401 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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_PresentationServiceBase_h -#define mozilla_dom_PresentationServiceBase_h - -#include "mozilla/Unused.h" -#include "nsClassHashtable.h" -#include "nsCOMArray.h" -#include "nsIPresentationListener.h" -#include "nsIPresentationService.h" -#include "nsRefPtrHashtable.h" -#include "nsString.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { - -template<class T> -class PresentationServiceBase -{ -public: - PresentationServiceBase() = default; - - already_AddRefed<T> - GetSessionInfo(const nsAString& aSessionId, const uint8_t aRole) - { - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<T> info; - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - return mSessionInfoAtController.Get(aSessionId, getter_AddRefs(info)) ? - info.forget() : nullptr; - } else { - return mSessionInfoAtReceiver.Get(aSessionId, getter_AddRefs(info)) ? - info.forget() : nullptr; - } - } - -protected: - class SessionIdManager final - { - public: - explicit SessionIdManager() - { - MOZ_COUNT_CTOR(SessionIdManager); - } - - ~SessionIdManager() - { - MOZ_COUNT_DTOR(SessionIdManager); - } - - nsresult GetWindowId(const nsAString& aSessionId, uint64_t* aWindowId) - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mRespondingWindowIds.Get(aSessionId, aWindowId)) { - return NS_OK; - } - return NS_ERROR_NOT_AVAILABLE; - } - - nsresult GetSessionIds(uint64_t aWindowId, nsTArray<nsString>& aSessionIds) - { - MOZ_ASSERT(NS_IsMainThread()); - - nsTArray<nsString>* sessionIdArray; - if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) { - return NS_ERROR_INVALID_ARG; - } - - aSessionIds.Assign(*sessionIdArray); - return NS_OK; - } - - void AddSessionId(uint64_t aWindowId, const nsAString& aSessionId) - { - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(aWindowId == 0)) { - return; - } - - nsTArray<nsString>* sessionIdArray; - if (!mRespondingSessionIds.Get(aWindowId, &sessionIdArray)) { - sessionIdArray = new nsTArray<nsString>(); - mRespondingSessionIds.Put(aWindowId, sessionIdArray); - } - - sessionIdArray->AppendElement(nsString(aSessionId)); - mRespondingWindowIds.Put(aSessionId, aWindowId); - } - - void RemoveSessionId(const nsAString& aSessionId) - { - MOZ_ASSERT(NS_IsMainThread()); - - uint64_t windowId = 0; - if (mRespondingWindowIds.Get(aSessionId, &windowId)) { - mRespondingWindowIds.Remove(aSessionId); - nsTArray<nsString>* sessionIdArray; - if (mRespondingSessionIds.Get(windowId, &sessionIdArray)) { - sessionIdArray->RemoveElement(nsString(aSessionId)); - if (sessionIdArray->IsEmpty()) { - mRespondingSessionIds.Remove(windowId); - } - } - } - } - - nsresult UpdateWindowId(const nsAString& aSessionId, const uint64_t aWindowId) - { - MOZ_ASSERT(NS_IsMainThread()); - - RemoveSessionId(aSessionId); - AddSessionId(aWindowId, aSessionId); - return NS_OK; - } - - void Clear() - { - mRespondingSessionIds.Clear(); - mRespondingWindowIds.Clear(); - } - - private: - nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mRespondingSessionIds; - nsDataHashtable<nsStringHashKey, uint64_t> mRespondingWindowIds; - }; - - class AvailabilityManager final - { - public: - explicit AvailabilityManager() - { - MOZ_COUNT_CTOR(AvailabilityManager); - } - - ~AvailabilityManager() - { - MOZ_COUNT_DTOR(AvailabilityManager); - } - - void AddAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) - { - nsTArray<nsString> dummy; - AddAvailabilityListener(aAvailabilityUrls, aListener, dummy); - } - - void AddAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener, - nsTArray<nsString>& aAddedUrls) - { - if (!aListener) { - MOZ_ASSERT(false, "aListener should not be null."); - return; - } - - if (aAvailabilityUrls.IsEmpty()) { - MOZ_ASSERT(false, "aAvailabilityUrls should not be empty."); - return; - } - - aAddedUrls.Clear(); - nsTArray<nsString> knownAvailableUrls; - for (const auto& url : aAvailabilityUrls) { - AvailabilityEntry* entry; - if (!mAvailabilityUrlTable.Get(url, &entry)) { - entry = new AvailabilityEntry(); - mAvailabilityUrlTable.Put(url, entry); - aAddedUrls.AppendElement(url); - } - if (!entry->mListeners.Contains(aListener)) { - entry->mListeners.AppendElement(aListener); - } - if (entry->mAvailable) { - knownAvailableUrls.AppendElement(url); - } - } - - if (!knownAvailableUrls.IsEmpty()) { - Unused << - NS_WARN_IF( - NS_FAILED(aListener->NotifyAvailableChange(knownAvailableUrls, - true))); - } else { - // If we can't find any known available url and there is no newly - // added url, we still need to notify the listener of the result. - // So, the promise returned by |getAvailability| can be resolved. - if (aAddedUrls.IsEmpty()) { - Unused << - NS_WARN_IF( - NS_FAILED(aListener->NotifyAvailableChange(aAvailabilityUrls, - false))); - } - } - } - - void RemoveAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) - { - nsTArray<nsString> dummy; - RemoveAvailabilityListener(aAvailabilityUrls, aListener, dummy); - } - - void RemoveAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener, - nsTArray<nsString>& aRemovedUrls) - { - if (!aListener) { - MOZ_ASSERT(false, "aListener should not be null."); - return; - } - - if (aAvailabilityUrls.IsEmpty()) { - MOZ_ASSERT(false, "aAvailabilityUrls should not be empty."); - return; - } - - aRemovedUrls.Clear(); - for (const auto& url : aAvailabilityUrls) { - AvailabilityEntry* entry; - if (mAvailabilityUrlTable.Get(url, &entry)) { - entry->mListeners.RemoveElement(aListener); - if (entry->mListeners.IsEmpty()) { - mAvailabilityUrlTable.Remove(url); - aRemovedUrls.AppendElement(url); - } - } - } - } - - nsresult DoNotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls, - bool aAvailable) - { - typedef nsClassHashtable<nsISupportsHashKey, - nsTArray<nsString>> ListenerToUrlsMap; - ListenerToUrlsMap availabilityListenerTable; - // Create a mapping from nsIPresentationAvailabilityListener to - // availabilityUrls. - for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) { - if (aAvailabilityUrls.Contains(it.Key())) { - AvailabilityEntry* entry = it.UserData(); - entry->mAvailable = aAvailable; - - for (uint32_t i = 0; i < entry->mListeners.Length(); ++i) { - nsIPresentationAvailabilityListener* listener = - entry->mListeners.ObjectAt(i); - nsTArray<nsString>* urlArray; - if (!availabilityListenerTable.Get(listener, &urlArray)) { - urlArray = new nsTArray<nsString>(); - availabilityListenerTable.Put(listener, urlArray); - } - urlArray->AppendElement(it.Key()); - } - } - } - - for (auto it = availabilityListenerTable.Iter(); !it.Done(); it.Next()) { - auto listener = - static_cast<nsIPresentationAvailabilityListener*>(it.Key()); - - Unused << - NS_WARN_IF(NS_FAILED(listener->NotifyAvailableChange(*it.UserData(), - aAvailable))); - } - return NS_OK; - } - - void GetAvailbilityUrlByAvailability(nsTArray<nsString>& aOutArray, - bool aAvailable) - { - aOutArray.Clear(); - - for (auto it = mAvailabilityUrlTable.ConstIter(); !it.Done(); it.Next()) { - if (it.UserData()->mAvailable == aAvailable) { - aOutArray.AppendElement(it.Key()); - } - } - } - - void Clear() - { - mAvailabilityUrlTable.Clear(); - } - - private: - struct AvailabilityEntry - { - explicit AvailabilityEntry() - : mAvailable(false) - {} - - bool mAvailable; - nsCOMArray<nsIPresentationAvailabilityListener> mListeners; - }; - - nsClassHashtable<nsStringHashKey, AvailabilityEntry> mAvailabilityUrlTable; - }; - - virtual ~PresentationServiceBase() = default; - - void Shutdown() - { - mRespondingListeners.Clear(); - mControllerSessionIdManager.Clear(); - mReceiverSessionIdManager.Clear(); - } - - nsresult GetWindowIdBySessionIdInternal(const nsAString& aSessionId, - uint8_t aRole, - uint64_t* aWindowId) - { - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - if (NS_WARN_IF(!aWindowId)) { - return NS_ERROR_INVALID_POINTER; - } - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - return mControllerSessionIdManager.GetWindowId(aSessionId, aWindowId); - } - - return mReceiverSessionIdManager.GetWindowId(aSessionId, aWindowId); - } - - void AddRespondingSessionId(uint64_t aWindowId, - const nsAString& aSessionId, - uint8_t aRole) - { - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - mControllerSessionIdManager.AddSessionId(aWindowId, aSessionId); - } else { - mReceiverSessionIdManager.AddSessionId(aWindowId, aSessionId); - } - } - - void RemoveRespondingSessionId(const nsAString& aSessionId, - uint8_t aRole) - { - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - mControllerSessionIdManager.RemoveSessionId(aSessionId); - } else { - mReceiverSessionIdManager.RemoveSessionId(aSessionId); - } - } - - nsresult UpdateWindowIdBySessionIdInternal(const nsAString& aSessionId, - uint8_t aRole, - const uint64_t aWindowId) - { - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - return mControllerSessionIdManager.UpdateWindowId(aSessionId, aWindowId); - } - - return mReceiverSessionIdManager.UpdateWindowId(aSessionId, aWindowId); - } - - // Store the responding listener based on the window ID of the (in-process or - // OOP) receiver page. - nsRefPtrHashtable<nsUint64HashKey, nsIPresentationRespondingListener> - mRespondingListeners; - - // Store the mapping between the window ID of the in-process and OOP page and the ID - // of the responding session. It's used for both controller and receiver page - // to retrieve the correspondent session ID. Besides, also keep the mapping - // between the responding session ID and the window ID to help look up the - // window ID. - SessionIdManager mControllerSessionIdManager; - SessionIdManager mReceiverSessionIdManager; - - nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtController; - nsRefPtrHashtable<nsStringHashKey, T> mSessionInfoAtReceiver; - - AvailabilityManager mAvailabilityManager; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationServiceBase_h diff --git a/dom/presentation/PresentationSessionInfo.cpp b/dom/presentation/PresentationSessionInfo.cpp deleted file mode 100644 index 1dd92ab69..000000000 --- a/dom/presentation/PresentationSessionInfo.cpp +++ /dev/null @@ -1,1617 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "mozilla/dom/ContentParent.h" -#include "mozilla/dom/HTMLIFrameElementBinding.h" -#include "mozilla/dom/TabParent.h" -#include "mozilla/Function.h" -#include "mozilla/Logging.h" -#include "mozilla/Move.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "nsContentUtils.h" -#include "nsGlobalWindow.h" -#include "nsIDocShell.h" -#include "nsFrameLoader.h" -#include "nsIMutableArray.h" -#include "nsINetAddr.h" -#include "nsISocketTransport.h" -#include "nsISupportsPrimitives.h" -#include "nsNetCID.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "PresentationLog.h" -#include "PresentationService.h" -#include "PresentationSessionInfo.h" - -#ifdef MOZ_WIDGET_ANDROID -#include "nsIPresentationNetworkHelper.h" -#endif // MOZ_WIDGET_ANDROID - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::services; - -/* - * Implementation of PresentationChannelDescription - */ - -namespace mozilla { -namespace dom { - -#ifdef MOZ_WIDGET_ANDROID - -namespace { - -class PresentationNetworkHelper final : public nsIPresentationNetworkHelperListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONNETWORKHELPERLISTENER - - using Function = nsresult(PresentationControllingInfo::*)(const nsACString&); - - explicit PresentationNetworkHelper(PresentationControllingInfo* aInfo, - const Function& aFunc); - - nsresult GetWifiIPAddress(); - -private: - ~PresentationNetworkHelper() = default; - - RefPtr<PresentationControllingInfo> mInfo; - Function mFunc; -}; - -NS_IMPL_ISUPPORTS(PresentationNetworkHelper, - nsIPresentationNetworkHelperListener) - -PresentationNetworkHelper::PresentationNetworkHelper(PresentationControllingInfo* aInfo, - const Function& aFunc) - : mInfo(aInfo) - , mFunc(aFunc) -{ - MOZ_ASSERT(aInfo); - MOZ_ASSERT(aFunc); -} - -nsresult -PresentationNetworkHelper::GetWifiIPAddress() -{ - nsresult rv; - - nsCOMPtr<nsIPresentationNetworkHelper> networkHelper = - do_GetService(PRESENTATION_NETWORK_HELPER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return networkHelper->GetWifiIPAddress(this); -} - -NS_IMETHODIMP -PresentationNetworkHelper::OnError(const nsACString & aReason) -{ - PRES_ERROR("PresentationNetworkHelper::OnError: %s", - nsPromiseFlatCString(aReason).get()); - return NS_OK; -} - -NS_IMETHODIMP -PresentationNetworkHelper::OnGetWifiIPAddress(const nsACString& aIPAddress) -{ - MOZ_ASSERT(mInfo); - MOZ_ASSERT(mFunc); - - NS_DispatchToMainThread( - NewRunnableMethod<nsCString>(mInfo, - mFunc, - aIPAddress)); - return NS_OK; -} - -} // anonymous namespace - -#endif // MOZ_WIDGET_ANDROID - -class TCPPresentationChannelDescription final : public nsIPresentationChannelDescription -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONCHANNELDESCRIPTION - - TCPPresentationChannelDescription(const nsACString& aAddress, - uint16_t aPort) - : mAddress(aAddress) - , mPort(aPort) - { - } - -private: - ~TCPPresentationChannelDescription() {} - - nsCString mAddress; - uint16_t mPort; -}; - -} // namespace dom -} // namespace mozilla - -NS_IMPL_ISUPPORTS(TCPPresentationChannelDescription, nsIPresentationChannelDescription) - -NS_IMETHODIMP -TCPPresentationChannelDescription::GetType(uint8_t* aRetVal) -{ - if (NS_WARN_IF(!aRetVal)) { - return NS_ERROR_INVALID_POINTER; - } - - *aRetVal = nsIPresentationChannelDescription::TYPE_TCP; - return NS_OK; -} - -NS_IMETHODIMP -TCPPresentationChannelDescription::GetTcpAddress(nsIArray** aRetVal) -{ - if (NS_WARN_IF(!aRetVal)) { - return NS_ERROR_INVALID_POINTER; - } - - nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID); - if (NS_WARN_IF(!array)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // TODO bug 1228504 Take all IP addresses in PresentationChannelDescription - // into account. And at the first stage Presentation API is only exposed on - // Firefox OS where the first IP appears enough for most scenarios. - nsCOMPtr<nsISupportsCString> address = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); - if (NS_WARN_IF(!address)) { - return NS_ERROR_OUT_OF_MEMORY; - } - address->SetData(mAddress); - - array->AppendElement(address, false); - array.forget(aRetVal); - - return NS_OK; -} - -NS_IMETHODIMP -TCPPresentationChannelDescription::GetTcpPort(uint16_t* aRetVal) -{ - if (NS_WARN_IF(!aRetVal)) { - return NS_ERROR_INVALID_POINTER; - } - - *aRetVal = mPort; - return NS_OK; -} - -NS_IMETHODIMP -TCPPresentationChannelDescription::GetDataChannelSDP(nsAString& aDataChannelSDP) -{ - aDataChannelSDP.Truncate(); - return NS_OK; -} - -/* - * Implementation of PresentationSessionInfo - */ - -NS_IMPL_ISUPPORTS(PresentationSessionInfo, - nsIPresentationSessionTransportCallback, - nsIPresentationControlChannelListener, - nsIPresentationSessionTransportBuilderListener); - -/* virtual */ nsresult -PresentationSessionInfo::Init(nsIPresentationControlChannel* aControlChannel) -{ - SetControlChannel(aControlChannel); - return NS_OK; -} - -/* virtual */ void -PresentationSessionInfo::Shutdown(nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole); - - NS_WARNING_ASSERTION(NS_SUCCEEDED(aReason), "bad reason"); - - // Close the control channel if any. - if (mControlChannel) { - Unused << NS_WARN_IF(NS_FAILED(mControlChannel->Disconnect(aReason))); - } - - // Close the data transport channel if any. - if (mTransport) { - // |mIsTransportReady| will be unset once |NotifyTransportClosed| is called. - Unused << NS_WARN_IF(NS_FAILED(mTransport->Close(aReason))); - } - - mIsResponderReady = false; - mIsOnTerminating = false; - - ResetBuilder(); -} - -nsresult -PresentationSessionInfo::SetListener(nsIPresentationSessionListener* aListener) -{ - mListener = aListener; - - if (mListener) { - // Enable data notification for the transport channel if it's available. - if (mTransport) { - nsresult rv = mTransport->EnableDataNotification(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - // The transport might become ready, or might become un-ready again, before - // the listener has registered. So notify the listener of the state change. - return mListener->NotifyStateChange(mSessionId, mState, mReason); - } - - return NS_OK; -} - -nsresult -PresentationSessionInfo::Send(const nsAString& aData) -{ - if (NS_WARN_IF(!IsSessionReady())) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->Send(aData); -} - -nsresult -PresentationSessionInfo::SendBinaryMsg(const nsACString& aData) -{ - if (NS_WARN_IF(!IsSessionReady())) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->SendBinaryMsg(aData); -} - -nsresult -PresentationSessionInfo::SendBlob(nsIDOMBlob* aBlob) -{ - if (NS_WARN_IF(!IsSessionReady())) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->SendBlob(aBlob); -} - -nsresult -PresentationSessionInfo::Close(nsresult aReason, - uint32_t aState) -{ - // Do nothing if session is already terminated. - if (nsIPresentationSessionListener::STATE_TERMINATED == mState) { - return NS_OK; - } - - SetStateWithReason(aState, aReason); - - switch (aState) { - case nsIPresentationSessionListener::STATE_CLOSED: { - Shutdown(aReason); - break; - } - case nsIPresentationSessionListener::STATE_TERMINATED: { - if (!mControlChannel) { - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - nsresult rv = mDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_FAILED(rv)) { - Shutdown(rv); - return rv; - } - - SetControlChannel(ctrlChannel); - return rv; - } - - ContinueTermination(); - return NS_OK; - } - } - - return NS_OK; -} - -nsresult -PresentationSessionInfo::OnTerminate(nsIPresentationControlChannel* aControlChannel) -{ - mIsOnTerminating = true; // Mark for terminating transport channel - SetStateWithReason(nsIPresentationSessionListener::STATE_TERMINATED, NS_OK); - SetControlChannel(aControlChannel); - - return NS_OK; -} - -nsresult -PresentationSessionInfo::ReplySuccess() -{ - SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTED, NS_OK); - return NS_OK; -} - -nsresult -PresentationSessionInfo::ReplyError(nsresult aError) -{ - Shutdown(aError); - - // Remove itself since it never succeeds. - return UntrackFromService(); -} - -/* virtual */ nsresult -PresentationSessionInfo::UntrackFromService() -{ - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId, mRole); - - return NS_OK; -} - -nsPIDOMWindowInner* -PresentationSessionInfo::GetWindow() -{ - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return nullptr; - } - uint64_t windowId = 0; - if (NS_WARN_IF(NS_FAILED(service->GetWindowIdBySessionId(mSessionId, - mRole, - &windowId)))) { - return nullptr; - } - - auto window = nsGlobalWindow::GetInnerWindowWithId(windowId); - if (!window) { - return nullptr; - } - - return window->AsInner(); -} - -/* virtual */ bool -PresentationSessionInfo::IsAccessible(base::ProcessId aProcessId) -{ - // No restriction by default. - return true; -} - -void -PresentationSessionInfo::ContinueTermination() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mControlChannel); - - if (NS_WARN_IF(NS_FAILED(mControlChannel->Terminate(mSessionId))) - || mIsOnTerminating) { - Shutdown(NS_OK); - } -} - -// nsIPresentationSessionTransportCallback -NS_IMETHODIMP -PresentationSessionInfo::NotifyTransportReady() -{ - PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState); - - MOZ_ASSERT(NS_IsMainThread()); - - if (mState != nsIPresentationSessionListener::STATE_CONNECTING && - mState != nsIPresentationSessionListener::STATE_CONNECTED) { - return NS_OK; - } - - mIsTransportReady = true; - - // Established RTCDataChannel implies responder is ready. - if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - mIsResponderReady = true; - } - - // At sender side, session might not be ready at this point (waiting for - // receiver's answer). Yet at receiver side, session must be ready at this - // point since the data transport channel is created after the receiver page - // is ready for presentation use. - if (IsSessionReady()) { - return ReplySuccess(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionInfo::NotifyTransportClosed(nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole); - - MOZ_ASSERT(NS_IsMainThread()); - - // Nullify |mTransport| here so it won't try to re-close |mTransport| in - // potential subsequent |Shutdown| calls. - mTransport = nullptr; - - if (NS_WARN_IF(!IsSessionReady() && - mState == nsIPresentationSessionListener::STATE_CONNECTING)) { - // It happens before the session is ready. Reply the callback. - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - // Unset |mIsTransportReady| here so it won't affect |IsSessionReady()| above. - mIsTransportReady = false; - - if (mState == nsIPresentationSessionListener::STATE_CONNECTED) { - // The transport channel is closed unexpectedly (not caused by a |Close| call). - SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aReason); - } - - Shutdown(aReason); - - if (mState == nsIPresentationSessionListener::STATE_TERMINATED) { - // Directly untrack the session info from the service. - return UntrackFromService(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionInfo::NotifyData(const nsACString& aData, bool aIsBinary) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!IsSessionReady())) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (NS_WARN_IF(!mListener)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mListener->NotifyMessage(mSessionId, aData, aIsBinary); -} - -// nsIPresentationSessionTransportBuilderListener -NS_IMETHODIMP -PresentationSessionInfo::OnSessionTransport(nsIPresentationSessionTransport* aTransport) -{ - PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState); - - ResetBuilder(); - - if (mState != nsIPresentationSessionListener::STATE_CONNECTING) { - return NS_ERROR_FAILURE; - } - - if (NS_WARN_IF(!aTransport)) { - return NS_ERROR_INVALID_ARG; - } - - mTransport = aTransport; - - nsresult rv = mTransport->SetCallback(this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mListener) { - mTransport->EnableDataNotification(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionInfo::OnError(nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole); - - ResetBuilder(); - return ReplyError(aReason); -} - -NS_IMETHODIMP -PresentationSessionInfo::SendOffer(nsIPresentationChannelDescription* aOffer) -{ - return mControlChannel->SendOffer(aOffer); -} - -NS_IMETHODIMP -PresentationSessionInfo::SendAnswer(nsIPresentationChannelDescription* aAnswer) -{ - return mControlChannel->SendAnswer(aAnswer); -} - -NS_IMETHODIMP -PresentationSessionInfo::SendIceCandidate(const nsAString& candidate) -{ - return mControlChannel->SendIceCandidate(candidate); -} - -NS_IMETHODIMP -PresentationSessionInfo::Close(nsresult reason) -{ - return mControlChannel->Disconnect(reason); -} - -/** - * Implementation of PresentationControllingInfo - * - * During presentation session establishment, the sender expects the following - * after trying to establish the control channel: (The order between step 3 and - * 4 is not guaranteed.) - * 1. |Init| is called to open a socket |mServerSocket| for data transport - * channel. - * 2. |NotifyConnected| of |nsIPresentationControlChannelListener| is called to - * indicate the control channel is ready to use. Then send the offer to the - * receiver via the control channel. - * 3.1 |OnSocketAccepted| of |nsIServerSocketListener| is called to indicate the - * data transport channel is connected. Then initialize |mTransport|. - * 3.2 |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is - * called. - * 4. |OnAnswer| of |nsIPresentationControlChannelListener| is called to - * indicate the receiver is ready. Close the control channel since it's no - * longer needed. - * 5. Once both step 3 and 4 are done, the presentation session is ready to use. - * So notify the listener of CONNECTED state. - */ - -NS_IMPL_ISUPPORTS_INHERITED(PresentationControllingInfo, - PresentationSessionInfo, - nsIServerSocketListener) - -nsresult -PresentationControllingInfo::Init(nsIPresentationControlChannel* aControlChannel) -{ - PresentationSessionInfo::Init(aControlChannel); - - // Initialize |mServerSocket| for bootstrapping the data transport channel and - // use |this| as the listener. - mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID); - if (NS_WARN_IF(!mServerSocket)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - nsresult rv = mServerSocket->Init(-1, false, -1); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mServerSocket->AsyncListen(this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int32_t port; - rv = mServerSocket->GetPort(&port); - if (!NS_WARN_IF(NS_FAILED(rv))) { - PRES_DEBUG("%s:ServerSocket created.port[%d]\n",__func__, port); - } - - return NS_OK; -} - -void -PresentationControllingInfo::Shutdown(nsresult aReason) -{ - PresentationSessionInfo::Shutdown(aReason); - - // Close the server socket if any. - if (mServerSocket) { - Unused << NS_WARN_IF(NS_FAILED(mServerSocket->Close())); - mServerSocket = nullptr; - } -} - -nsresult -PresentationControllingInfo::GetAddress() -{ -#if defined(MOZ_WIDGET_ANDROID) - RefPtr<PresentationNetworkHelper> networkHelper = - new PresentationNetworkHelper(this, - &PresentationControllingInfo::OnGetAddress); - nsresult rv = networkHelper->GetWifiIPAddress(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - -#else - nsCOMPtr<nsINetworkInfoService> networkInfo = do_GetService(NETWORKINFOSERVICE_CONTRACT_ID); - MOZ_ASSERT(networkInfo); - - nsresult rv = networkInfo->ListNetworkAddresses(this); - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } -#endif - - return NS_OK; -} - -nsresult -PresentationControllingInfo::OnGetAddress(const nsACString& aAddress) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!mServerSocket)) { - return NS_ERROR_FAILURE; - } - if (NS_WARN_IF(!mControlChannel)) { - return NS_ERROR_FAILURE; - } - - // Prepare and send the offer. - int32_t port; - nsresult rv = mServerSocket->GetPort(&port); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<TCPPresentationChannelDescription> description = - new TCPPresentationChannelDescription(aAddress, static_cast<uint16_t>(port)); - return mControlChannel->SendOffer(description); -} - -// nsIPresentationControlChannelListener -NS_IMETHODIMP -PresentationControllingInfo::OnIceCandidate(const nsAString& aCandidate) -{ - if (mTransportType != nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - builder = do_QueryInterface(mBuilder); - - if (NS_WARN_IF(!builder)) { - return NS_ERROR_FAILURE; - } - - return builder->OnIceCandidate(aCandidate); -} - -NS_IMETHODIMP -PresentationControllingInfo::OnOffer(nsIPresentationChannelDescription* aDescription) -{ - MOZ_ASSERT(false, "Sender side should not receive offer."); - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -PresentationControllingInfo::OnAnswer(nsIPresentationChannelDescription* aDescription) -{ - if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - builder = do_QueryInterface(mBuilder); - - if (NS_WARN_IF(!builder)) { - return NS_ERROR_FAILURE; - } - - return builder->OnAnswer(aDescription); - } - - mIsResponderReady = true; - - // Close the control channel since it's no longer needed. - nsresult rv = mControlChannel->Disconnect(NS_OK); - if (NS_WARN_IF(NS_FAILED(rv))) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - // Session might not be ready at this moment (waiting for the establishment of - // the data transport channel). - if (IsSessionReady()){ - return ReplySuccess(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationControllingInfo::NotifyConnected() -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole); - - MOZ_ASSERT(NS_IsMainThread()); - - switch (mState) { - case nsIPresentationSessionListener::STATE_CONNECTING: { - if (mIsReconnecting) { - return ContinueReconnect(); - } - - nsresult rv = mControlChannel->Launch(GetSessionId(), GetUrl()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - Unused << NS_WARN_IF(NS_FAILED(BuildTransport())); - break; - } - case nsIPresentationSessionListener::STATE_TERMINATED: { - ContinueTermination(); - break; - } - default: - break; - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationControllingInfo::NotifyReconnected() -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole); - - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(mState != nsIPresentationSessionListener::STATE_CONNECTING)) { - return NS_ERROR_FAILURE; - } - - return NotifyReconnectResult(NS_OK); -} - -nsresult -PresentationControllingInfo::BuildTransport() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mState != nsIPresentationSessionListener::STATE_CONNECTING) { - return NS_OK; - } - - if (NS_WARN_IF(!mBuilderConstructor)) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) { - // Build TCP session transport - return GetAddress(); - } - /** - * Generally transport is maintained by the chrome process. However, data - * channel should be live with the DOM , which implies RTCDataChannel in an OOP - * page should be establish in the content process. - * - * |mBuilderConstructor| is responsible for creating a builder, which is for - * building a data channel transport. - * - * In the OOP case, |mBuilderConstructor| would create a builder which is - * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport| - * triggers an IPC call to make content process establish a RTCDataChannel - * transport. - */ - - mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL; - if (NS_WARN_IF(NS_FAILED( - mBuilderConstructor->CreateTransportBuilder(mTransportType, - getter_AddRefs(mBuilder))))) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - dataChannelBuilder(do_QueryInterface(mBuilder)); - if (NS_WARN_IF(!dataChannelBuilder)) { - return NS_ERROR_NOT_AVAILABLE; - } - - // OOP window would be set from content process - nsPIDOMWindowInner* window = GetWindow(); - - nsresult rv = dataChannelBuilder-> - BuildDataChannelTransport(nsIPresentationService::ROLE_CONTROLLER, - window, - this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationControllingInfo::NotifyDisconnected(nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole); - - MOZ_ASSERT(NS_IsMainThread()); - - if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - builder = do_QueryInterface(mBuilder); - if (builder) { - Unused << NS_WARN_IF(NS_FAILED(builder->NotifyDisconnected(aReason))); - } - } - - // Unset control channel here so it won't try to re-close it in potential - // subsequent |Shutdown| calls. - SetControlChannel(nullptr); - - if (NS_WARN_IF(NS_FAILED(aReason) || !mIsResponderReady)) { - // The presentation session instance may already exist. - // Change the state to CLOSED if it is not terminated. - if (nsIPresentationSessionListener::STATE_TERMINATED != mState) { - SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aReason); - } - - // If |aReason| is NS_OK, it implies that the user closes the connection - // before becomming connected. No need to call |ReplyError| in this case. - if (NS_FAILED(aReason)) { - if (mIsReconnecting) { - NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR); - } - // Reply error for an abnormal close. - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - Shutdown(aReason); - } - - // This is the case for reconnecting a connection which is in - // connecting state and |mTransport| is not ready. - if (mDoReconnectAfterClose && !mTransport) { - mDoReconnectAfterClose = false; - return Reconnect(mReconnectCallback); - } - - return NS_OK; -} - -// nsIServerSocketListener -NS_IMETHODIMP -PresentationControllingInfo::OnSocketAccepted(nsIServerSocket* aServerSocket, - nsISocketTransport* aTransport) -{ - int32_t port; - nsresult rv = aTransport->GetPort(&port); - if (!NS_WARN_IF(NS_FAILED(rv))) { - PRES_DEBUG("%s:receive from port[%d]\n",__func__, port); - } - - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!mBuilderConstructor)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - // Initialize session transport builder and use |this| as the callback. - nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder; - if (NS_SUCCEEDED(mBuilderConstructor->CreateTransportBuilder( - nsIPresentationChannelDescription::TYPE_TCP, - getter_AddRefs(mBuilder)))) { - builder = do_QueryInterface(mBuilder); - } - - if (NS_WARN_IF(!builder)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - mTransportType = nsIPresentationChannelDescription::TYPE_TCP; - return builder->BuildTCPSenderTransport(aTransport, this); -} - -NS_IMETHODIMP -PresentationControllingInfo::OnStopListening(nsIServerSocket* aServerSocket, - nsresult aStatus) -{ - PRES_DEBUG("controller %s:status[%x]\n",__func__, aStatus); - - MOZ_ASSERT(NS_IsMainThread()); - - if (aStatus == NS_BINDING_ABORTED) { // The server socket was manually closed. - return NS_OK; - } - - Shutdown(aStatus); - - if (NS_WARN_IF(!IsSessionReady())) { - // It happens before the session is ready. Reply the callback. - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - // It happens after the session is ready. Change the state to CLOSED. - SetStateWithReason(nsIPresentationSessionListener::STATE_CLOSED, aStatus); - - return NS_OK; -} - -/** - * The steps to reconnect a session are summarized below: - * 1. Change |mState| to CONNECTING. - * 2. Check whether |mControlChannel| is existed or not. Usually we have to - * create a new control cahnnel. - * 3.1 |mControlChannel| is null, which means we have to create a new one. - * |EstablishControlChannel| is called to create a new control channel. - * At this point, |mControlChannel| is not able to use yet. Set - * |mIsReconnecting| to true and wait until |NotifyConnected|. - * 3.2 |mControlChannel| is not null and is avaliable. - * We can just call |ContinueReconnect| to send reconnect command. - * 4. |NotifyReconnected| of |nsIPresentationControlChannelListener| is called - * to indicate the receiver is ready for reconnecting. - * 5. Once both step 3 and 4 are done, the rest is to build a new data - * transport channel by following the same steps as starting a - * new session. - */ - -nsresult -PresentationControllingInfo::Reconnect(nsIPresentationServiceCallback* aCallback) -{ - PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState); - - if (!aCallback) { - return NS_ERROR_INVALID_ARG; - } - - mReconnectCallback = aCallback; - - if (NS_WARN_IF(mState == nsIPresentationSessionListener::STATE_TERMINATED)) { - return NotifyReconnectResult(NS_ERROR_DOM_INVALID_STATE_ERR); - } - - // If |mState| is not CLOSED, we have to close the connection before - // reconnecting. The process to reconnect will be continued after - // |NotifyDisconnected| or |NotifyTransportClosed| is invoked. - if (mState == nsIPresentationSessionListener::STATE_CONNECTING || - mState == nsIPresentationSessionListener::STATE_CONNECTED) { - mDoReconnectAfterClose = true; - return Close(NS_OK, nsIPresentationSessionListener::STATE_CLOSED); - } - - // Make sure |mState| is closed at this point. - MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED); - - mState = nsIPresentationSessionListener::STATE_CONNECTING; - mIsReconnecting = true; - - nsresult rv = NS_OK; - if (!mControlChannel) { - nsCOMPtr<nsIPresentationControlChannel> ctrlChannel; - rv = mDevice->EstablishControlChannel(getter_AddRefs(ctrlChannel)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR); - } - - rv = Init(ctrlChannel); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR); - } - } else { - return ContinueReconnect(); - } - - return NS_OK; -} - -nsresult -PresentationControllingInfo::ContinueReconnect() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mControlChannel); - - mIsReconnecting = false; - if (NS_WARN_IF(NS_FAILED(mControlChannel->Reconnect(mSessionId, GetUrl())))) { - return NotifyReconnectResult(NS_ERROR_DOM_OPERATION_ERR); - } - - return NS_OK; -} - -// nsIListNetworkAddressesListener -NS_IMETHODIMP -PresentationControllingInfo::OnListedNetworkAddresses(const char** aAddressArray, - uint32_t aAddressArraySize) -{ - if (!aAddressArraySize) { - return OnListNetworkAddressesFailed(); - } - - // TODO bug 1228504 Take all IP addresses in PresentationChannelDescription - // into account. And at the first stage Presentation API is only exposed on - // Firefox OS where the first IP appears enough for most scenarios. - - nsAutoCString ip; - ip.Assign(aAddressArray[0]); - - // On Firefox desktop, the IP address is retrieved from a callback function. - // To make consistent code sequence, following function call is dispatched - // into main thread instead of calling it directly. - NS_DispatchToMainThread( - NewRunnableMethod<nsCString>( - this, - &PresentationControllingInfo::OnGetAddress, - ip)); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationControllingInfo::OnListNetworkAddressesFailed() -{ - PRES_ERROR("PresentationControllingInfo:OnListNetworkAddressesFailed"); - - // In 1-UA case, transport channel can still be established - // on loopback interface even if no network address available. - NS_DispatchToMainThread( - NewRunnableMethod<nsCString>( - this, - &PresentationControllingInfo::OnGetAddress, - "127.0.0.1")); - - return NS_OK; -} - -nsresult -PresentationControllingInfo::NotifyReconnectResult(nsresult aStatus) -{ - if (!mReconnectCallback) { - MOZ_ASSERT(false, "mReconnectCallback can not be null here."); - return NS_ERROR_FAILURE; - } - - mIsReconnecting = false; - nsCOMPtr<nsIPresentationServiceCallback> callback = - mReconnectCallback.forget(); - if (NS_FAILED(aStatus)) { - return callback->NotifyError(aStatus); - } - - return callback->NotifySuccess(GetUrl()); -} - -// nsIPresentationSessionTransportCallback -NS_IMETHODIMP -PresentationControllingInfo::NotifyTransportReady() -{ - return PresentationSessionInfo::NotifyTransportReady(); -} - -NS_IMETHODIMP -PresentationControllingInfo::NotifyTransportClosed(nsresult aReason) -{ - if (!mDoReconnectAfterClose) { - return PresentationSessionInfo::NotifyTransportClosed(aReason);; - } - - MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED); - - mTransport = nullptr; - mIsTransportReady = false; - mDoReconnectAfterClose = false; - return Reconnect(mReconnectCallback); -} - -NS_IMETHODIMP -PresentationControllingInfo::NotifyData(const nsACString& aData, bool aIsBinary) -{ - return PresentationSessionInfo::NotifyData(aData, aIsBinary); -} - -/** - * Implementation of PresentationPresentingInfo - * - * During presentation session establishment, the receiver expects the following - * after trying to launch the app by notifying "presentation-launch-receiver": - * (The order between step 2 and 3 is not guaranteed.) - * 1. |Observe| of |nsIObserver| is called with "presentation-receiver-launched". - * Then start listen to document |STATE_TRANSFERRING| event. - * 2. |NotifyResponderReady| is called to indicate the receiver page is ready - * for presentation use. - * 3. |OnOffer| of |nsIPresentationControlChannelListener| is called. - * 4. Once both step 2 and 3 are done, establish the data transport channel and - * send the answer. (The control channel will be closed by the sender once it - * receives the answer.) - * 5. |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is - * called. The presentation session is ready to use, so notify the listener - * of CONNECTED state. - */ - -NS_IMPL_ISUPPORTS_INHERITED(PresentationPresentingInfo, - PresentationSessionInfo, - nsITimerCallback) - -nsresult -PresentationPresentingInfo::Init(nsIPresentationControlChannel* aControlChannel) -{ - PresentationSessionInfo::Init(aControlChannel); - - // Add a timer to prevent waiting indefinitely in case the receiver page fails - // to become ready. - nsresult rv; - int32_t timeout = - Preferences::GetInt("presentation.receiver.loading.timeout", 10000); - mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = mTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -PresentationPresentingInfo::Shutdown(nsresult aReason) -{ - PresentationSessionInfo::Shutdown(aReason); - - if (mTimer) { - mTimer->Cancel(); - } - - mLoadingCallback = nullptr; - mRequesterDescription = nullptr; - mPendingCandidates.Clear(); - mPromise = nullptr; - mHasFlushPendingEvents = false; -} - -// nsIPresentationSessionTransportBuilderListener -NS_IMETHODIMP -PresentationPresentingInfo::OnSessionTransport(nsIPresentationSessionTransport* aTransport) -{ - nsresult rv = PresentationSessionInfo::OnSessionTransport(aTransport); - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // The session transport is managed by content process - if (NS_WARN_IF(!aTransport)) { - return NS_ERROR_INVALID_ARG; - } - - // send answer for TCP session transport - if (mTransportType == nsIPresentationChannelDescription::TYPE_TCP) { - // Prepare and send the answer. - // In the current implementation of |PresentationSessionTransport|, - // |GetSelfAddress| cannot return the real info when it's initialized via - // |buildTCPReceiverTransport|. Yet this deficiency only affects the channel - // description for the answer, which is not actually checked at requester side. - nsCOMPtr<nsINetAddr> selfAddr; - rv = mTransport->GetSelfAddress(getter_AddRefs(selfAddr)); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetSelfAddress failed"); - - nsCString address; - uint16_t port = 0; - if (NS_SUCCEEDED(rv)) { - selfAddr->GetAddress(address); - selfAddr->GetPort(&port); - } - nsCOMPtr<nsIPresentationChannelDescription> description = - new TCPPresentationChannelDescription(address, port); - - return mControlChannel->SendAnswer(description); - } - - return NS_OK; -} - -// Delegate the pending offer and ICE candidates to builder. -NS_IMETHODIMP -PresentationPresentingInfo::FlushPendingEvents(nsIPresentationDataChannelSessionTransportBuilder* builder) -{ - if (NS_WARN_IF(!builder)) { - return NS_ERROR_FAILURE; - } - - mHasFlushPendingEvents = true; - - if (mRequesterDescription) { - builder->OnOffer(mRequesterDescription); - } - mRequesterDescription = nullptr; - - for (size_t i = 0; i < mPendingCandidates.Length(); ++i) { - builder->OnIceCandidate(mPendingCandidates[i]); - } - mPendingCandidates.Clear(); - return NS_OK; -} - -nsresult -PresentationPresentingInfo::InitTransportAndSendAnswer() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CONNECTING); - - uint8_t type = 0; - nsresult rv = mRequesterDescription->GetType(&type); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!mBuilderConstructor)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - if (NS_WARN_IF(NS_FAILED( - mBuilderConstructor->CreateTransportBuilder(type, - getter_AddRefs(mBuilder))))) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (type == nsIPresentationChannelDescription::TYPE_TCP) { - // Establish a data transport channel |mTransport| to the sender and use - // |this| as the callback. - nsCOMPtr<nsIPresentationTCPSessionTransportBuilder> builder = - do_QueryInterface(mBuilder); - if (NS_WARN_IF(!builder)) { - return NS_ERROR_NOT_AVAILABLE; - } - - mTransportType = nsIPresentationChannelDescription::TYPE_TCP; - return builder->BuildTCPReceiverTransport(mRequesterDescription, this); - } - - if (type == nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - if (!Preferences::GetBool("dom.presentation.session_transport.data_channel.enable")) { - return NS_ERROR_NOT_IMPLEMENTED; - } - /** - * Generally transport is maintained by the chrome process. However, data - * channel should be live with the DOM , which implies RTCDataChannel in an OOP - * page should be establish in the content process. - * - * |mBuilderConstructor| is responsible for creating a builder, which is for - * building a data channel transport. - * - * In the OOP case, |mBuilderConstructor| would create a builder which is - * an object of |PresentationBuilderParent|. So, |BuildDataChannelTransport| - * triggers an IPC call to make content process establish a RTCDataChannel - * transport. - */ - - mTransportType = nsIPresentationChannelDescription::TYPE_DATACHANNEL; - - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> dataChannelBuilder = - do_QueryInterface(mBuilder); - if (NS_WARN_IF(!dataChannelBuilder)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsPIDOMWindowInner* window = GetWindow(); - - rv = dataChannelBuilder-> - BuildDataChannelTransport(nsIPresentationService::ROLE_RECEIVER, - window, - this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = FlushPendingEvents(dataChannelBuilder); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; - } - - MOZ_ASSERT(false, "Unknown nsIPresentationChannelDescription type!"); - return NS_ERROR_UNEXPECTED; -} - -nsresult -PresentationPresentingInfo::UntrackFromService() -{ - // Remove the OOP responding info (if it has never been used). - if (mContentParent) { - Unused << NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverCleanUp(mSessionId)); - } - - // Receiver device might need clean up after session termination. - if (mDevice) { - mDevice->Disconnect(); - } - mDevice = nullptr; - - // Remove the session info (and the in-process responding info if there's any). - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - static_cast<PresentationService*>(service.get())->UntrackSessionInfo(mSessionId, mRole); - - return NS_OK; -} - -bool -PresentationPresentingInfo::IsAccessible(base::ProcessId aProcessId) -{ - // Only the specific content process should access the responder info. - return (mContentParent) ? - aProcessId == static_cast<ContentParent*>(mContentParent.get())->OtherPid() : - false; -} - -nsresult -PresentationPresentingInfo::NotifyResponderReady() -{ - PRES_DEBUG("%s:id[%s], role[%d], state[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole, mState); - - if (mTimer) { - mTimer->Cancel(); - mTimer = nullptr; - } - - mIsResponderReady = true; - - // Initialize |mTransport| and send the answer to the sender if sender's - // description is already offered. - if (mRequesterDescription) { - nsresult rv = InitTransportAndSendAnswer(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - } - - return NS_OK; -} - -nsresult -PresentationPresentingInfo::NotifyResponderFailure() -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole); - - if (mTimer) { - mTimer->Cancel(); - mTimer = nullptr; - } - - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); -} - -nsresult -PresentationPresentingInfo::DoReconnect() -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole); - - MOZ_ASSERT(mState == nsIPresentationSessionListener::STATE_CLOSED); - - SetStateWithReason(nsIPresentationSessionListener::STATE_CONNECTING, NS_OK); - - return NotifyResponderReady(); -} - -// nsIPresentationControlChannelListener -NS_IMETHODIMP -PresentationPresentingInfo::OnOffer(nsIPresentationChannelDescription* aDescription) -{ - if (NS_WARN_IF(mHasFlushPendingEvents)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - if (NS_WARN_IF(!aDescription)) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - mRequesterDescription = aDescription; - - // Initialize |mTransport| and send the answer to the sender if the receiver - // page is ready for presentation use. - if (mIsResponderReady) { - nsresult rv = InitTransportAndSendAnswer(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationPresentingInfo::OnAnswer(nsIPresentationChannelDescription* aDescription) -{ - MOZ_ASSERT(false, "Receiver side should not receive answer."); - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -PresentationPresentingInfo::OnIceCandidate(const nsAString& aCandidate) -{ - if (!mBuilder && !mHasFlushPendingEvents) { - mPendingCandidates.AppendElement(nsString(aCandidate)); - return NS_OK; - } - - if (NS_WARN_IF(!mBuilder && mHasFlushPendingEvents)) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - builder = do_QueryInterface(mBuilder); - - return builder->OnIceCandidate(aCandidate); -} - -NS_IMETHODIMP -PresentationPresentingInfo::NotifyConnected() -{ - PRES_DEBUG("%s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), mRole); - - if (nsIPresentationSessionListener::STATE_TERMINATED == mState) { - ContinueTermination(); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationPresentingInfo::NotifyReconnected() -{ - MOZ_ASSERT(false, "NotifyReconnected should not be called at receiver side."); - return NS_OK; -} - -NS_IMETHODIMP -PresentationPresentingInfo::NotifyDisconnected(nsresult aReason) -{ - PRES_DEBUG("%s:id[%s], reason[%x], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(mSessionId).get(), aReason, mRole); - - MOZ_ASSERT(NS_IsMainThread()); - - if (mTransportType == nsIPresentationChannelDescription::TYPE_DATACHANNEL) { - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> - builder = do_QueryInterface(mBuilder); - if (builder) { - Unused << NS_WARN_IF(NS_FAILED(builder->NotifyDisconnected(aReason))); - } - } - - // Unset control channel here so it won't try to re-close it in potential - // subsequent |Shutdown| calls. - SetControlChannel(nullptr); - - if (NS_WARN_IF(NS_FAILED(aReason))) { - // The presentation session instance may already exist. - // Change the state to TERMINATED since it never succeeds. - SetStateWithReason(nsIPresentationSessionListener::STATE_TERMINATED, aReason); - - // Reply error for an abnormal close. - return ReplyError(NS_ERROR_DOM_OPERATION_ERR); - } - - return NS_OK; -} - -// nsITimerCallback -NS_IMETHODIMP -PresentationPresentingInfo::Notify(nsITimer* aTimer) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_WARNING("The receiver page fails to become ready before timeout."); - - mTimer = nullptr; - return ReplyError(NS_ERROR_DOM_TIMEOUT_ERR); -} - -// PromiseNativeHandler -void -PresentationPresentingInfo::ResolvedCallback(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aValue.isObject())) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - JS::Rooted<JSObject*> obj(aCx, &aValue.toObject()); - if (NS_WARN_IF(!obj)) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - // Start to listen to document state change event |STATE_TRANSFERRING|. - // Use Element to support both HTMLIFrameElement and nsXULElement. - Element* frame = nullptr; - nsresult rv = UNWRAP_OBJECT(Element, &obj, frame); - if (NS_WARN_IF(!frame)) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface((nsIFrameLoaderOwner*) frame); - if (NS_WARN_IF(!owner)) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - nsCOMPtr<nsIFrameLoader> frameLoader = owner->GetFrameLoader(); - if (NS_WARN_IF(!frameLoader)) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - RefPtr<TabParent> tabParent = TabParent::GetFrom(frameLoader); - if (tabParent) { - // OOP frame - // Notify the content process that a receiver page has launched, so it can - // start monitoring the loading progress. - mContentParent = tabParent->Manager(); - Unused << NS_WARN_IF(!static_cast<ContentParent*>(mContentParent.get())->SendNotifyPresentationReceiverLaunched(tabParent, mSessionId)); - } else { - // In-process frame - nsCOMPtr<nsIDocShell> docShell; - rv = frameLoader->GetDocShell(getter_AddRefs(docShell)); - if (NS_WARN_IF(NS_FAILED(rv))) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - // Keep an eye on the loading progress of the receiver page. - mLoadingCallback = new PresentationResponderLoadingCallback(mSessionId); - rv = mLoadingCallback->Init(docShell); - if (NS_WARN_IF(NS_FAILED(rv))) { - ReplyError(NS_ERROR_DOM_OPERATION_ERR); - return; - } - } -} - -void -PresentationPresentingInfo::RejectedCallback(JSContext* aCx, - JS::Handle<JS::Value> aValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - NS_WARNING("Launching the receiver page has been rejected."); - - if (mTimer) { - mTimer->Cancel(); - mTimer = nullptr; - } - - ReplyError(NS_ERROR_DOM_OPERATION_ERR); -} diff --git a/dom/presentation/PresentationSessionInfo.h b/dom/presentation/PresentationSessionInfo.h deleted file mode 100644 index 6338d3c32..000000000 --- a/dom/presentation/PresentationSessionInfo.h +++ /dev/null @@ -1,304 +0,0 @@ -/* -*- 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_PresentationSessionInfo_h -#define mozilla_dom_PresentationSessionInfo_h - -#include "base/process.h" -#include "mozilla/dom/nsIContentParent.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/PromiseNativeHandler.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "nsINetworkInfoService.h" -#include "nsIPresentationControlChannel.h" -#include "nsIPresentationDevice.h" -#include "nsIPresentationListener.h" -#include "nsIPresentationService.h" -#include "nsIPresentationSessionTransport.h" -#include "nsIPresentationSessionTransportBuilder.h" -#include "nsIServerSocket.h" -#include "nsITimer.h" -#include "nsString.h" -#include "PresentationCallbacks.h" - -namespace mozilla { -namespace dom { - -class PresentationSessionInfo : public nsIPresentationSessionTransportCallback - , public nsIPresentationControlChannelListener - , public nsIPresentationSessionTransportBuilderListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER - - PresentationSessionInfo(const nsAString& aUrl, - const nsAString& aSessionId, - const uint8_t aRole) - : mUrl(aUrl) - , mSessionId(aSessionId) - , mIsResponderReady(false) - , mIsTransportReady(false) - , mState(nsIPresentationSessionListener::STATE_CONNECTING) - , mReason(NS_OK) - { - MOZ_ASSERT(!mUrl.IsEmpty()); - MOZ_ASSERT(!mSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - mRole = aRole; - } - - virtual nsresult Init(nsIPresentationControlChannel* aControlChannel); - - const nsAString& GetUrl() const - { - return mUrl; - } - - const nsAString& GetSessionId() const - { - return mSessionId; - } - - uint8_t GetRole() const - { - return mRole; - } - - nsresult SetListener(nsIPresentationSessionListener* aListener); - - void SetDevice(nsIPresentationDevice* aDevice) - { - mDevice = aDevice; - } - - already_AddRefed<nsIPresentationDevice> GetDevice() const - { - nsCOMPtr<nsIPresentationDevice> device = mDevice; - return device.forget(); - } - - void SetControlChannel(nsIPresentationControlChannel* aControlChannel) - { - if (mControlChannel) { - mControlChannel->SetListener(nullptr); - } - - mControlChannel = aControlChannel; - if (mControlChannel) { - mControlChannel->SetListener(this); - } - } - - nsresult Send(const nsAString& aData); - - nsresult SendBinaryMsg(const nsACString& aData); - - nsresult SendBlob(nsIDOMBlob* aBlob); - - nsresult Close(nsresult aReason, - uint32_t aState); - - nsresult OnTerminate(nsIPresentationControlChannel* aControlChannel); - - nsresult ReplyError(nsresult aReason); - - virtual bool IsAccessible(base::ProcessId aProcessId); - - void SetTransportBuilderConstructor( - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) - { - mBuilderConstructor = aBuilderConstructor; - } - -protected: - virtual ~PresentationSessionInfo() - { - Shutdown(NS_OK); - } - - virtual void Shutdown(nsresult aReason); - - nsresult ReplySuccess(); - - bool IsSessionReady() - { - return mIsResponderReady && mIsTransportReady; - } - - virtual nsresult UntrackFromService(); - - void SetStateWithReason(uint32_t aState, nsresult aReason) - { - if (mState == aState) { - return; - } - - mState = aState; - mReason = aReason; - - // Notify session state change. - if (mListener) { - DebugOnly<nsresult> rv = - mListener->NotifyStateChange(mSessionId, mState, aReason); - NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyStateChanged"); - } - } - - void ContinueTermination(); - - void ResetBuilder() - { - mBuilder = nullptr; - } - - // Should be nsIPresentationChannelDescription::TYPE_TCP/TYPE_DATACHANNEL - uint8_t mTransportType = 0; - - nsPIDOMWindowInner* GetWindow(); - - nsString mUrl; - nsString mSessionId; - // mRole should be nsIPresentationService::ROLE_CONTROLLER - // or nsIPresentationService::ROLE_RECEIVER. - uint8_t mRole; - bool mIsResponderReady; - bool mIsTransportReady; - bool mIsOnTerminating = false; - uint32_t mState; // CONNECTED, CLOSED, TERMINATED - nsresult mReason; - nsCOMPtr<nsIPresentationSessionListener> mListener; - nsCOMPtr<nsIPresentationDevice> mDevice; - nsCOMPtr<nsIPresentationSessionTransport> mTransport; - nsCOMPtr<nsIPresentationControlChannel> mControlChannel; - nsCOMPtr<nsIPresentationSessionTransportBuilder> mBuilder; - nsCOMPtr<nsIPresentationTransportBuilderConstructor> mBuilderConstructor; -}; - -// Session info with controlling browsing context (sender side) behaviors. -class PresentationControllingInfo final : public PresentationSessionInfo - , public nsIServerSocketListener - , public nsIListNetworkAddressesListener -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER - NS_DECL_NSISERVERSOCKETLISTENER - NS_DECL_NSILISTNETWORKADDRESSESLISTENER - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK - - PresentationControllingInfo(const nsAString& aUrl, - const nsAString& aSessionId) - : PresentationSessionInfo(aUrl, - aSessionId, - nsIPresentationService::ROLE_CONTROLLER) - {} - - nsresult Init(nsIPresentationControlChannel* aControlChannel) override; - - nsresult Reconnect(nsIPresentationServiceCallback* aCallback); - - nsresult BuildTransport(); - -private: - ~PresentationControllingInfo() - { - Shutdown(NS_OK); - } - - void Shutdown(nsresult aReason) override; - - nsresult GetAddress(); - - nsresult OnGetAddress(const nsACString& aAddress); - - nsresult ContinueReconnect(); - - nsresult NotifyReconnectResult(nsresult aStatus); - - nsCOMPtr<nsIServerSocket> mServerSocket; - nsCOMPtr<nsIPresentationServiceCallback> mReconnectCallback; - bool mIsReconnecting = false; - bool mDoReconnectAfterClose = false; -}; - -// Session info with presenting browsing context (receiver side) behaviors. -class PresentationPresentingInfo final : public PresentationSessionInfo - , public PromiseNativeHandler - , public nsITimerCallback -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER - NS_DECL_NSITIMERCALLBACK - - PresentationPresentingInfo(const nsAString& aUrl, - const nsAString& aSessionId, - nsIPresentationDevice* aDevice) - : PresentationSessionInfo(aUrl, - aSessionId, - nsIPresentationService::ROLE_RECEIVER) - { - MOZ_ASSERT(aDevice); - SetDevice(aDevice); - } - - nsresult Init(nsIPresentationControlChannel* aControlChannel) override; - - nsresult NotifyResponderReady(); - nsresult NotifyResponderFailure(); - - NS_IMETHODIMP OnSessionTransport(nsIPresentationSessionTransport* transport) override; - - void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; - - void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; - - void SetPromise(Promise* aPromise) - { - mPromise = aPromise; - mPromise->AppendNativeHandler(this); - } - - bool IsAccessible(base::ProcessId aProcessId) override; - - nsresult DoReconnect(); - -private: - ~PresentationPresentingInfo() - { - Shutdown(NS_OK); - } - - void Shutdown(nsresult aReason) override; - - nsresult InitTransportAndSendAnswer(); - - nsresult UntrackFromService() override; - - NS_IMETHODIMP - FlushPendingEvents(nsIPresentationDataChannelSessionTransportBuilder* builder); - - bool mHasFlushPendingEvents = false; - RefPtr<PresentationResponderLoadingCallback> mLoadingCallback; - nsCOMPtr<nsITimer> mTimer; - nsCOMPtr<nsIPresentationChannelDescription> mRequesterDescription; - nsTArray<nsString> mPendingCandidates; - RefPtr<Promise> mPromise; - - // The content parent communicating with the content process which the OOP - // receiver page belongs to. - nsCOMPtr<nsIContentParent> mContentParent; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationSessionInfo_h diff --git a/dom/presentation/PresentationSessionRequest.cpp b/dom/presentation/PresentationSessionRequest.cpp deleted file mode 100644 index 219fbd6a4..000000000 --- a/dom/presentation/PresentationSessionRequest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- 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 "PresentationSessionRequest.h" -#include "nsIPresentationControlChannel.h" -#include "nsIPresentationDevice.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(PresentationSessionRequest, nsIPresentationSessionRequest) - -PresentationSessionRequest::PresentationSessionRequest(nsIPresentationDevice* aDevice, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) - : mUrl(aUrl) - , mPresentationId(aPresentationId) - , mDevice(aDevice) - , mControlChannel(aControlChannel) -{ -} - -PresentationSessionRequest::~PresentationSessionRequest() -{ -} - -// nsIPresentationSessionRequest - -NS_IMETHODIMP -PresentationSessionRequest::GetDevice(nsIPresentationDevice** aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - - nsCOMPtr<nsIPresentationDevice> device = mDevice; - device.forget(aRetVal); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionRequest::GetUrl(nsAString& aRetVal) -{ - aRetVal = mUrl; - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionRequest::GetPresentationId(nsAString& aRetVal) -{ - aRetVal = mPresentationId; - - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionRequest::GetControlChannel(nsIPresentationControlChannel** aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - - nsCOMPtr<nsIPresentationControlChannel> controlChannel = mControlChannel; - controlChannel.forget(aRetVal); - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationSessionRequest.h b/dom/presentation/PresentationSessionRequest.h deleted file mode 100644 index c56502d77..000000000 --- a/dom/presentation/PresentationSessionRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- 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_PresentationSessionRequest_h__ -#define mozilla_dom_PresentationSessionRequest_h__ - -#include "nsIPresentationSessionRequest.h" -#include "nsCOMPtr.h" -#include "nsString.h" - -namespace mozilla { -namespace dom { - -class PresentationSessionRequest final : public nsIPresentationSessionRequest -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONREQUEST - - PresentationSessionRequest(nsIPresentationDevice* aDevice, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel); - -private: - virtual ~PresentationSessionRequest(); - - nsString mUrl; - nsString mPresentationId; - nsCOMPtr<nsIPresentationDevice> mDevice; - nsCOMPtr<nsIPresentationControlChannel> mControlChannel; -}; - -} // namespace dom -} // namespace mozilla - -#endif /* mozilla_dom_PresentationSessionRequest_h__ */ - diff --git a/dom/presentation/PresentationTCPSessionTransport.cpp b/dom/presentation/PresentationTCPSessionTransport.cpp deleted file mode 100644 index 1ccb8b43c..000000000 --- a/dom/presentation/PresentationTCPSessionTransport.cpp +++ /dev/null @@ -1,589 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "nsArrayUtils.h" -#include "nsIAsyncStreamCopier.h" -#include "nsIInputStreamPump.h" -#include "nsIMultiplexInputStream.h" -#include "nsIMutableArray.h" -#include "nsIOutputStream.h" -#include "nsIPresentationControlChannel.h" -#include "nsIScriptableInputStream.h" -#include "nsISocketTransport.h" -#include "nsISocketTransportService.h" -#include "nsISupportsPrimitives.h" -#include "nsNetUtil.h" -#include "nsQueryObject.h" -#include "nsServiceManagerUtils.h" -#include "nsStreamUtils.h" -#include "nsThreadUtils.h" -#include "PresentationLog.h" -#include "PresentationTCPSessionTransport.h" - -#define BUFFER_SIZE 65536 - -using namespace mozilla; -using namespace mozilla::dom; - -class CopierCallbacks final : public nsIRequestObserver -{ -public: - explicit CopierCallbacks(PresentationTCPSessionTransport* aTransport) - : mOwner(aTransport) - {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER -private: - ~CopierCallbacks() {} - - RefPtr<PresentationTCPSessionTransport> mOwner; -}; - -NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver) - -NS_IMETHODIMP -CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) -{ - return NS_OK; -} - -NS_IMETHODIMP -CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus) -{ - mOwner->NotifyCopyComplete(aStatus); - return NS_OK; -} - -NS_IMPL_CYCLE_COLLECTION(PresentationTCPSessionTransport, mTransport, - mSocketInputStream, mSocketOutputStream, - mInputStreamPump, mInputStreamScriptable, - mMultiplexStream, mMultiplexStreamCopier, mCallback) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationTCPSessionTransport) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationTCPSessionTransport) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationTCPSessionTransport) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPresentationSessionTransport) - NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) - NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransport) - NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransportBuilder) - NS_INTERFACE_MAP_ENTRY(nsIPresentationTCPSessionTransportBuilder) - NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) - NS_INTERFACE_MAP_ENTRY(nsIStreamListener) - NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) -NS_INTERFACE_MAP_END - -PresentationTCPSessionTransport::PresentationTCPSessionTransport() - : mReadyState(ReadyState::CLOSED) - , mAsyncCopierActive(false) - , mCloseStatus(NS_OK) - , mDataNotificationEnabled(false) -{ -} - -PresentationTCPSessionTransport::~PresentationTCPSessionTransport() -{ -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::BuildTCPSenderTransport(nsISocketTransport* aTransport, - nsIPresentationSessionTransportBuilderListener* aListener) -{ - if (NS_WARN_IF(!aTransport)) { - return NS_ERROR_INVALID_ARG; - } - mTransport = aTransport; - - if (NS_WARN_IF(!aListener)) { - return NS_ERROR_INVALID_ARG; - } - mListener = aListener; - - nsresult rv = CreateStream(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mRole = nsIPresentationService::ROLE_CONTROLLER; - - nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this); - nsCOMPtr<nsIRunnable> onSessionTransportRunnable = - NewRunnableMethod - <nsIPresentationSessionTransport*>(mListener, - &nsIPresentationSessionTransportBuilderListener::OnSessionTransport, - sessionTransport); - - NS_DispatchToCurrentThread(onSessionTransportRunnable.forget()); - - nsCOMPtr<nsIRunnable> setReadyStateRunnable = - NewRunnableMethod<ReadyState>(this, - &PresentationTCPSessionTransport::SetReadyState, - ReadyState::OPEN); - return NS_DispatchToCurrentThread(setReadyStateRunnable.forget()); -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::BuildTCPReceiverTransport(nsIPresentationChannelDescription* aDescription, - nsIPresentationSessionTransportBuilderListener* aListener) -{ - if (NS_WARN_IF(!aDescription)) { - return NS_ERROR_INVALID_ARG; - } - - if (NS_WARN_IF(!aListener)) { - return NS_ERROR_INVALID_ARG; - } - mListener = aListener; - - uint16_t serverPort; - nsresult rv = aDescription->GetTcpPort(&serverPort); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsIArray> serverHosts; - rv = aDescription->GetTcpAddress(getter_AddRefs(serverHosts)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // TODO bug 1228504 Take all IP addresses in PresentationChannelDescription - // into account. And at the first stage Presentation API is only exposed on - // Firefox OS where the first IP appears enough for most scenarios. - nsCOMPtr<nsISupportsCString> supportStr = do_QueryElementAt(serverHosts, 0); - if (NS_WARN_IF(!supportStr)) { - return NS_ERROR_INVALID_ARG; - } - - nsAutoCString serverHost; - supportStr->GetData(serverHost); - if (serverHost.IsEmpty()) { - return NS_ERROR_INVALID_ARG; - } - - PRES_DEBUG("%s:ServerHost[%s],ServerPort[%d]\n", __func__, serverHost.get(), serverPort); - - SetReadyState(ReadyState::CONNECTING); - - nsCOMPtr<nsISocketTransportService> sts = - do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); - if (NS_WARN_IF(!sts)) { - return NS_ERROR_NOT_AVAILABLE; - } - rv = sts->CreateTransport(nullptr, 0, serverHost, serverPort, nullptr, - getter_AddRefs(mTransport)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsIThread> mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - mTransport->SetEventSink(this, mainThread); - - rv = CreateStream(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mRole = nsIPresentationService::ROLE_RECEIVER; - - nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this); - nsCOMPtr<nsIRunnable> runnable = - NewRunnableMethod - <nsIPresentationSessionTransport*>(mListener, - &nsIPresentationSessionTransportBuilderListener::OnSessionTransport, - sessionTransport); - return NS_DispatchToCurrentThread(runnable.forget()); -} - -nsresult -PresentationTCPSessionTransport::CreateStream() -{ - nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // If the other side is not listening, we will get an |onInputStreamReady| - // callback where |available| raises to indicate the connection was refused. - nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream); - if (NS_WARN_IF(!asyncStream)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIThread> mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - - rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainThread); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = mInputStreamScriptable->Init(mSocketInputStream); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsISocketTransportService> sts = - do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); - if (NS_WARN_IF(!sts)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts); - rv = mMultiplexStreamCopier->Init(mMultiplexStream, - mSocketOutputStream, - target, - true, /* source buffered */ - false, /* sink buffered */ - BUFFER_SIZE, - false, /* close source */ - false); /* close sink */ - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -PresentationTCPSessionTransport::CreateInputStreamPump() -{ - if (NS_WARN_IF(mInputStreamPump)) { - return NS_OK; - } - - nsresult rv; - mInputStreamPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mInputStreamPump->AsyncRead(this, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::EnableDataNotification() -{ - if (NS_WARN_IF(!mCallback)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - if (mDataNotificationEnabled) { - return NS_OK; - } - - mDataNotificationEnabled = true; - - if (IsReadyToNotifyData()) { - return CreateInputStreamPump(); - } - - return NS_OK; -} - -// nsIPresentationSessionTransportBuilderListener -NS_IMETHODIMP -PresentationTCPSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback) -{ - nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback; - callback.forget(aCallback); - return NS_OK; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback) -{ - mCallback = aCallback; - - if (!!mCallback && ReadyState::OPEN == mReadyState) { - // Notify the transport channel is ready. - Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady())); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress) -{ - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - return mTransport->GetScriptableSelfAddr(aSelfAddress); -} - -void -PresentationTCPSessionTransport::EnsureCopying() -{ - if (mAsyncCopierActive) { - return; - } - - mAsyncCopierActive = true; - RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this); - Unused << NS_WARN_IF(NS_FAILED(mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr))); -} - -void -PresentationTCPSessionTransport::NotifyCopyComplete(nsresult aStatus) -{ - mAsyncCopierActive = false; - mMultiplexStream->RemoveStream(0); - if (NS_WARN_IF(NS_FAILED(aStatus))) { - if (mReadyState != ReadyState::CLOSED) { - mCloseStatus = aStatus; - SetReadyState(ReadyState::CLOSED); - } - return; - } - - uint32_t count; - nsresult rv = mMultiplexStream->GetCount(&count); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - if (count) { - EnsureCopying(); - return; - } - - if (mReadyState == ReadyState::CLOSING) { - mSocketOutputStream->Close(); - mCloseStatus = NS_OK; - SetReadyState(ReadyState::CLOSED); - } -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::Send(const nsAString& aData) -{ - if (NS_WARN_IF(mReadyState != ReadyState::OPEN)) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - nsresult rv; - nsCOMPtr<nsIStringInputStream> stream = - do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv); - if(NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - NS_ConvertUTF16toUTF8 msgString(aData); - rv = stream->SetData(msgString.BeginReading(), msgString.Length()); - if(NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - mMultiplexStream->AppendStream(stream); - - EnsureCopying(); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::SendBinaryMsg(const nsACString& aData) -{ - return NS_ERROR_DOM_NOT_SUPPORTED_ERR; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::SendBlob(nsIDOMBlob* aBlob) -{ - return NS_ERROR_DOM_NOT_SUPPORTED_ERR; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::Close(nsresult aReason) -{ - PRES_DEBUG("%s:reason[%x]\n", __func__, aReason); - - if (mReadyState == ReadyState::CLOSED || mReadyState == ReadyState::CLOSING) { - return NS_OK; - } - - mCloseStatus = aReason; - SetReadyState(ReadyState::CLOSING); - - uint32_t count = 0; - mMultiplexStream->GetCount(&count); - if (!count) { - mSocketOutputStream->Close(); - } - - mSocketInputStream->Close(); - mDataNotificationEnabled = false; - - mListener = nullptr; - - return NS_OK; -} - -void -PresentationTCPSessionTransport::SetReadyState(ReadyState aReadyState) -{ - mReadyState = aReadyState; - - if (mReadyState == ReadyState::OPEN) { - if (IsReadyToNotifyData()) { - CreateInputStreamPump(); - } - - if (NS_WARN_IF(!mCallback)) { - return; - } - - // Notify the transport channel is ready. - Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady())); - } else if (mReadyState == ReadyState::CLOSED && mCallback) { - if (NS_WARN_IF(!mCallback)) { - return; - } - - // Notify the transport channel has been shut down. - Unused << - NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus))); - mCallback = nullptr; - } -} - -// nsITransportEventSink -NS_IMETHODIMP -PresentationTCPSessionTransport::OnTransportStatus(nsITransport* aTransport, - nsresult aStatus, - int64_t aProgress, - int64_t aProgressMax) -{ - PRES_DEBUG("%s:aStatus[%x]\n", __func__, aStatus); - - MOZ_ASSERT(NS_IsMainThread()); - - if (aStatus != NS_NET_STATUS_CONNECTED_TO) { - return NS_OK; - } - - SetReadyState(ReadyState::OPEN); - - return NS_OK; -} - -// nsIInputStreamCallback -NS_IMETHODIMP -PresentationTCPSessionTransport::OnInputStreamReady(nsIAsyncInputStream* aStream) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // Only used for detecting if the connection was refused. - uint64_t dummy; - nsresult rv = aStream->Available(&dummy); - if (NS_WARN_IF(NS_FAILED(rv))) { - if (mReadyState != ReadyState::CLOSED) { - mCloseStatus = NS_ERROR_CONNECTION_REFUSED; - SetReadyState(ReadyState::CLOSED); - } - } - - return NS_OK; -} - -// nsIRequestObserver -NS_IMETHODIMP -PresentationTCPSessionTransport::OnStartRequest(nsIRequest* aRequest, - nsISupports* aContext) -{ - // Do nothing. - return NS_OK; -} - -NS_IMETHODIMP -PresentationTCPSessionTransport::OnStopRequest(nsIRequest* aRequest, - nsISupports* aContext, - nsresult aStatusCode) -{ - PRES_DEBUG("%s:aStatusCode[%x]\n", __func__, aStatusCode); - - MOZ_ASSERT(NS_IsMainThread()); - - uint32_t count; - nsresult rv = mMultiplexStream->GetCount(&count); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mInputStreamPump = nullptr; - - if (count != 0 && NS_SUCCEEDED(aStatusCode)) { - // If we have some buffered output still, and status is not an error, the - // other side has done a half-close, but we don't want to be in the close - // state until we are done sending everything that was buffered. We also - // don't want to call |NotifyTransportClosed| yet. - return NS_OK; - } - - // We call this even if there is no error. - if (mReadyState != ReadyState::CLOSED) { - mCloseStatus = aStatusCode; - SetReadyState(ReadyState::CLOSED); - } - return NS_OK; -} - -// nsIStreamListener -NS_IMETHODIMP -PresentationTCPSessionTransport::OnDataAvailable(nsIRequest* aRequest, - nsISupports* aContext, - nsIInputStream* aStream, - uint64_t aOffset, - uint32_t aCount) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!mCallback)) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCString data; - nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Pass the incoming data to the listener. - return mCallback->NotifyData(data, false); -} diff --git a/dom/presentation/PresentationTCPSessionTransport.h b/dom/presentation/PresentationTCPSessionTransport.h deleted file mode 100644 index f36b371a4..000000000 --- a/dom/presentation/PresentationTCPSessionTransport.h +++ /dev/null @@ -1,110 +0,0 @@ -/* -*- 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_PresentationSessionTransport_h -#define mozilla_dom_PresentationSessionTransport_h - -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "nsIAsyncInputStream.h" -#include "nsIPresentationSessionTransport.h" -#include "nsIPresentationSessionTransportBuilder.h" -#include "nsIStreamListener.h" -#include "nsISupportsImpl.h" -#include "nsITransport.h" - -class nsISocketTransport; -class nsIInputStreamPump; -class nsIScriptableInputStream; -class nsIMultiplexInputStream; -class nsIAsyncStreamCopier; -class nsIInputStream; - -namespace mozilla { -namespace dom { - -/* - * App-to-App transport channel for the presentation session. It's usually - * initialized with an |InitWithSocketTransport| call if at the presenting sender - * side; whereas it's initialized with an |InitWithChannelDescription| if at the - * presenting receiver side. The lifetime is managed in either - * |PresentationControllingInfo| (sender side) or |PresentationPresentingInfo| - * (receiver side) in PresentationSessionInfo.cpp. - */ -class PresentationTCPSessionTransport final : public nsIPresentationSessionTransport - , public nsIPresentationTCPSessionTransportBuilder - , public nsITransportEventSink - , public nsIInputStreamCallback - , public nsIStreamListener -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PresentationTCPSessionTransport, - nsIPresentationSessionTransport) - - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDER - NS_DECL_NSIPRESENTATIONTCPSESSIONTRANSPORTBUILDER - NS_DECL_NSITRANSPORTEVENTSINK - NS_DECL_NSIINPUTSTREAMCALLBACK - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - - PresentationTCPSessionTransport(); - - void NotifyCopyComplete(nsresult aStatus); - -private: - ~PresentationTCPSessionTransport(); - - nsresult CreateStream(); - - nsresult CreateInputStreamPump(); - - void EnsureCopying(); - - enum class ReadyState { - CONNECTING, - OPEN, - CLOSING, - CLOSED - }; - - void SetReadyState(ReadyState aReadyState); - - bool IsReadyToNotifyData() - { - return mDataNotificationEnabled && mReadyState == ReadyState::OPEN; - } - - ReadyState mReadyState; - bool mAsyncCopierActive; - nsresult mCloseStatus; - bool mDataNotificationEnabled; - - uint8_t mRole = 0; - - // Raw socket streams - nsCOMPtr<nsISocketTransport> mTransport; - nsCOMPtr<nsIInputStream> mSocketInputStream; - nsCOMPtr<nsIOutputStream> mSocketOutputStream; - - // Input stream machinery - nsCOMPtr<nsIInputStreamPump> mInputStreamPump; - nsCOMPtr<nsIScriptableInputStream> mInputStreamScriptable; - - // Output stream machinery - nsCOMPtr<nsIMultiplexInputStream> mMultiplexStream; - nsCOMPtr<nsIAsyncStreamCopier> mMultiplexStreamCopier; - - nsCOMPtr<nsIPresentationSessionTransportCallback> mCallback; - nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mListener; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationSessionTransport_h diff --git a/dom/presentation/PresentationTerminateRequest.cpp b/dom/presentation/PresentationTerminateRequest.cpp deleted file mode 100644 index 61fd8403f..000000000 --- a/dom/presentation/PresentationTerminateRequest.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- 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 "PresentationTerminateRequest.h" -#include "nsIPresentationControlChannel.h" -#include "nsIPresentationDevice.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(PresentationTerminateRequest, nsIPresentationTerminateRequest) - -PresentationTerminateRequest::PresentationTerminateRequest( - nsIPresentationDevice* aDevice, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel, - bool aIsFromReceiver) - : mPresentationId(aPresentationId) - , mDevice(aDevice) - , mControlChannel(aControlChannel) - , mIsFromReceiver(aIsFromReceiver) -{ -} - -PresentationTerminateRequest::~PresentationTerminateRequest() -{ -} - -// nsIPresentationTerminateRequest -NS_IMETHODIMP -PresentationTerminateRequest::GetDevice(nsIPresentationDevice** aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - - nsCOMPtr<nsIPresentationDevice> device = mDevice; - device.forget(aRetVal); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTerminateRequest::GetPresentationId(nsAString& aRetVal) -{ - aRetVal = mPresentationId; - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTerminateRequest::GetControlChannel( - nsIPresentationControlChannel** aRetVal) -{ - NS_ENSURE_ARG_POINTER(aRetVal); - - nsCOMPtr<nsIPresentationControlChannel> controlChannel = mControlChannel; - controlChannel.forget(aRetVal); - - return NS_OK; -} - -NS_IMETHODIMP -PresentationTerminateRequest::GetIsFromReceiver(bool* aRetVal) -{ - *aRetVal = mIsFromReceiver; - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationTerminateRequest.h b/dom/presentation/PresentationTerminateRequest.h deleted file mode 100644 index ca3563f8d..000000000 --- a/dom/presentation/PresentationTerminateRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- 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_PresentationTerminateRequest_h__ -#define mozilla_dom_PresentationTerminateRequest_h__ - -#include "nsIPresentationTerminateRequest.h" -#include "nsCOMPtr.h" -#include "nsString.h" - -namespace mozilla { -namespace dom { - -class PresentationTerminateRequest final : public nsIPresentationTerminateRequest -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONTERMINATEREQUEST - - PresentationTerminateRequest(nsIPresentationDevice* aDevice, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel, - bool aIsFromReceiver); - -private: - virtual ~PresentationTerminateRequest(); - - nsString mPresentationId; - nsCOMPtr<nsIPresentationDevice> mDevice; - nsCOMPtr<nsIPresentationControlChannel> mControlChannel; - bool mIsFromReceiver; -}; - -} // namespace dom -} // namespace mozilla - -#endif /* mozilla_dom_PresentationTerminateRequest_h__ */ - diff --git a/dom/presentation/PresentationTransportBuilderConstructor.cpp b/dom/presentation/PresentationTransportBuilderConstructor.cpp deleted file mode 100644 index 98177958d..000000000 --- a/dom/presentation/PresentationTransportBuilderConstructor.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* -*- 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 "PresentationTransportBuilderConstructor.h" - -#include "nsComponentManagerUtils.h" -#include "nsIPresentationControlChannel.h" -#include "nsIPresentationSessionTransport.h" -#include "nsXULAppAPI.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(DummyPresentationTransportBuilderConstructor, - nsIPresentationTransportBuilderConstructor) - -NS_IMETHODIMP -DummyPresentationTransportBuilderConstructor::CreateTransportBuilder( - uint8_t aType, - nsIPresentationSessionTransportBuilder** aRetval) -{ - MOZ_ASSERT(false, "Unexpected to be invoked."); - return NS_OK; -} - -NS_IMPL_ISUPPORTS_INHERITED0(PresentationTransportBuilderConstructor, - DummyPresentationTransportBuilderConstructor) - -/* static */ already_AddRefed<nsIPresentationTransportBuilderConstructor> -PresentationTransportBuilderConstructor::Create() -{ - nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor; - if (XRE_IsContentProcess()) { - constructor = new DummyPresentationTransportBuilderConstructor(); - } else { - constructor = new PresentationTransportBuilderConstructor(); - } - - return constructor.forget(); -} - -NS_IMETHODIMP -PresentationTransportBuilderConstructor::CreateTransportBuilder( - uint8_t aType, - nsIPresentationSessionTransportBuilder** aRetval) -{ - if (NS_WARN_IF(!aRetval)) { - return NS_ERROR_INVALID_ARG; - } - - *aRetval = nullptr; - - if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP && - aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) { - return NS_ERROR_INVALID_ARG; - } - - if (XRE_IsContentProcess()) { - MOZ_ASSERT(false, - "CreateTransportBuilder can only be invoked in parent process."); - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIPresentationSessionTransportBuilder> builder; - if (aType == nsIPresentationChannelDescription::TYPE_TCP) { - builder = - do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID); - } else { - builder = - do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1"); - } - - if (NS_WARN_IF(!builder)) { - return NS_ERROR_DOM_OPERATION_ERR; - } - - builder.forget(aRetval); - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/PresentationTransportBuilderConstructor.h b/dom/presentation/PresentationTransportBuilderConstructor.h deleted file mode 100644 index 4250d61ba..000000000 --- a/dom/presentation/PresentationTransportBuilderConstructor.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- 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_PresentationTransportBuilderConstructor_h -#define mozilla_dom_PresentationTransportBuilderConstructor_h - -#include "nsCOMPtr.h" -#include "nsIPresentationSessionTransportBuilder.h" -#include "nsISupportsImpl.h" - -namespace mozilla { -namespace dom { - -class DummyPresentationTransportBuilderConstructor : - public nsIPresentationTransportBuilderConstructor -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR - - DummyPresentationTransportBuilderConstructor() = default; - -protected: - virtual ~DummyPresentationTransportBuilderConstructor() = default; -}; - -class PresentationTransportBuilderConstructor final : - public DummyPresentationTransportBuilderConstructor -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR - - static already_AddRefed<nsIPresentationTransportBuilderConstructor> - Create(); - -private: - PresentationTransportBuilderConstructor() = default; - virtual ~PresentationTransportBuilderConstructor() = default; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationTransportBuilderConstructor_h diff --git a/dom/presentation/interfaces/moz.build b/dom/presentation/interfaces/moz.build deleted file mode 100644 index 935e39000..000000000 --- a/dom/presentation/interfaces/moz.build +++ /dev/null @@ -1,30 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -XPIDL_SOURCES += [ - 'nsIPresentationControlChannel.idl', - 'nsIPresentationControlService.idl', - 'nsIPresentationDevice.idl', - 'nsIPresentationDeviceManager.idl', - 'nsIPresentationDevicePrompt.idl', - 'nsIPresentationDeviceProvider.idl', - 'nsIPresentationListener.idl', - 'nsIPresentationLocalDevice.idl', - 'nsIPresentationRequestUIGlue.idl', - 'nsIPresentationService.idl', - 'nsIPresentationSessionRequest.idl', - 'nsIPresentationSessionTransport.idl', - 'nsIPresentationSessionTransportBuilder.idl', - 'nsIPresentationTerminateRequest.idl', -] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - XPIDL_SOURCES += [ - 'nsIPresentationNetworkHelper.idl', - ] - -XPIDL_MODULE = 'dom_presentation' - diff --git a/dom/presentation/interfaces/nsIPresentationControlChannel.idl b/dom/presentation/interfaces/nsIPresentationControlChannel.idl deleted file mode 100644 index 669e4088e..000000000 --- a/dom/presentation/interfaces/nsIPresentationControlChannel.idl +++ /dev/null @@ -1,139 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIArray; -interface nsIInputStream; - -[scriptable, uuid(ae318e05-2a4e-4f85-95c0-e8b191ad812c)] -interface nsIPresentationChannelDescription: nsISupports -{ - const unsigned short TYPE_TCP = 1; - const unsigned short TYPE_DATACHANNEL = 2; - - // Type of transport channel. - readonly attribute uint8_t type; - - // Addresses for TCP channel (as a list of nsISupportsCString). - // Should only be used while type == TYPE_TCP. - readonly attribute nsIArray tcpAddress; - - // Port number for TCP channel. - // Should only be used while type == TYPE_TCP. - readonly attribute uint16_t tcpPort; - - // SDP for Data Channel. - // Should only be used while type == TYPE_DATACHANNEL. - readonly attribute DOMString dataChannelSDP; -}; - -/* - * The callbacks for events on control channel. - */ -[scriptable, uuid(96dd548f-7d0f-43c1-b1ad-28e666cf1e82)] -interface nsIPresentationControlChannelListener: nsISupports -{ - /* - * Callback for receiving offer from remote endpoint. - * @param offer The received offer. - */ - void onOffer(in nsIPresentationChannelDescription offer); - - /* - * Callback for receiving answer from remote endpoint. - * @param answer The received answer. - */ - void onAnswer(in nsIPresentationChannelDescription answer); - - /* - * Callback for receiving ICE candidate from remote endpoint. - * @param answer The received answer. - */ - void onIceCandidate(in DOMString candidate); - - /* - * The callback for notifying channel connected. This should be async called - * after nsIPresentationDevice::establishControlChannel. - */ - void notifyConnected(); - - /* - * The callback for notifying channel disconnected. - * @param reason The reason of channel close, NS_OK represents normal close. - */ - void notifyDisconnected(in nsresult reason); - - /* - * The callback for notifying the reconnect command is acknowledged. - */ - void notifyReconnected(); -}; - -/* - * The control channel for establishing RTCPeerConnection for a presentation - * session. SDP Offer/Answer will be exchanged through this interface. The - * control channel should be in-order. - */ -[scriptable, uuid(e60e208c-a9f5-4bc6-9a3e-47f3e4ae9c57)] -interface nsIPresentationControlChannel: nsISupports -{ - // The listener for handling events of this control channel. - // All the events should be pending until listener is assigned. - attribute nsIPresentationControlChannelListener listener; - - /* - * Send offer to remote endpoint. |onOffer| should be invoked on remote - * endpoint. - * @param offer The offer to send. - * @throws NS_ERROR_FAILURE on failure - */ - void sendOffer(in nsIPresentationChannelDescription offer); - - /* - * Send answer to remote endpoint. |onAnswer| should be invoked on remote - * endpoint. - * @param answer The answer to send. - * @throws NS_ERROR_FAILURE on failure - */ - void sendAnswer(in nsIPresentationChannelDescription answer); - - /* - * Send ICE candidate to remote endpoint. |onIceCandidate| should be invoked - * on remote endpoint. - * @param candidate The candidate to send - * @throws NS_ERROR_FAILURE on failure - */ - void sendIceCandidate(in DOMString candidate); - - /* - * Launch a presentation on remote endpoint. - * @param presentationId The Id for representing this session. - * @param url The URL requested to open by remote device. - * @throws NS_ERROR_FAILURE on failure - */ - void launch(in DOMString presentationId, in DOMString url); - - /* - * Terminate a presentation on remote endpoint. - * @param presentationId The Id for representing this session. - * @throws NS_ERROR_FAILURE on failure - */ - void terminate(in DOMString presentationId); - - /* - * Disconnect the control channel. - * @param reason The reason of disconnecting channel; NS_OK represents normal. - */ - void disconnect(in nsresult reason); - - /* - * Reconnect a presentation on remote endpoint. - * Note that only controller is allowed to reconnect a session. - * @param presentationId The Id for representing this session. - * @param url The URL requested to open by remote device. - * @throws NS_ERROR_FAILURE on failure - */ - void reconnect(in DOMString presentationId, in DOMString url); -}; diff --git a/dom/presentation/interfaces/nsIPresentationControlService.idl b/dom/presentation/interfaces/nsIPresentationControlService.idl deleted file mode 100644 index d4b967b00..000000000 --- a/dom/presentation/interfaces/nsIPresentationControlService.idl +++ /dev/null @@ -1,156 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationControlChannel; - -%{C++ -#define PRESENTATION_CONTROL_SERVICE_CONTACT_ID \ - "@mozilla.org/presentation/control-service;1" -%} - -/* - * The device information required for establishing control channel. - */ -[scriptable, uuid(296fd171-e4d0-4de0-99ff-ad8ed52ddef3)] -interface nsITCPDeviceInfo: nsISupports -{ - readonly attribute AUTF8String id; - readonly attribute AUTF8String address; - readonly attribute uint16_t port; - // SHA-256 fingerprint of server certificate. Empty string represents - // server doesn't support TLS or not available. - readonly attribute AUTF8String certFingerprint; -}; - -[scriptable, uuid(09bddfaf-fcc2-4dc9-b33e-a509a1c2fb6d)] -interface nsIPresentationControlServerListener: nsISupports -{ - /** - * Callback while the server is ready or restarted. - * @param aPort - * The port of the server socket. - * @param aCertFingerprint - * The SHA-256 fingerprint of TLS server certificate. - * Empty string represents server started without encryption. - */ - void onServerReady(in uint16_t aPort, in AUTF8String aCertFingerprint); - - /** - * Callback while the server is stopped or fails to start. - * @param aResult - * The error cause of server stopped or the reason of - * start failure. - * NS_OK means the server is stopped by close. - */ - void onServerStopped(in nsresult aResult); - - /** - * Callback while the remote host is requesting to start a presentation session. - * @param aDeviceInfo The device information related to the remote host. - * @param aUrl The URL requested to open by remote device. - * @param aPresentationId The Id for representing this session. - * @param aControlChannel The control channel for this session. - */ - void onSessionRequest(in nsITCPDeviceInfo aDeviceInfo, - in DOMString aUrl, - in DOMString aPresentationId, - in nsIPresentationControlChannel aControlChannel); - - /** - * Callback while the remote host is requesting to terminate a presentation session. - * @param aDeviceInfo The device information related to the remote host. - * @param aPresentationId The Id for representing this session. - * @param aControlChannel The control channel for this session. - * @param aIsFromReceiver true if termination is initiated by receiver. - */ - void onTerminateRequest(in nsITCPDeviceInfo aDeviceInfo, - in DOMString aPresentationId, - in nsIPresentationControlChannel aControlChannel, - in boolean aIsFromReceiver); - - /** - * Callback while the remote host is requesting to reconnect a presentation session. - * @param aDeviceInfo The device information related to the remote host. - * @param aUrl The URL requested to open by remote device. - * @param aPresentationId The Id for representing this session. - * @param aControlChannel The control channel for this session. - */ - void onReconnectRequest(in nsITCPDeviceInfo aDeviceInfo, - in DOMString url, - in DOMString aPresentationId, - in nsIPresentationControlChannel aControlChannel); -}; - -/** - * Presentation control service which can be used for both presentation - * control client and server. - */ -[scriptable, uuid(55d6b605-2389-4aae-a8fe-60d4440540ea)] -interface nsIPresentationControlService: nsISupports -{ - /** - * This method initializes server socket. Caller should set listener and - * monitor onServerReady event to get the correct server info. - * @param aEncrypted - * True for using TLS control channel. - * @param aPort - * The port of the server socket. Pass 0 or opt-out to indicate no - * preference, and a port will be selected automatically. - * @throws NS_ERROR_FAILURE if the server socket has been inited or the - * server socket can not be inited. - */ - void startServer(in boolean aEncrypted, [optional] in uint16_t aPort); - - /** - * Request connection to designated remote presentation control receiver. - * @param aDeviceInfo - * The remtoe device info for establish connection. - * @returns The control channel for this session. - * @throws NS_ERROR_FAILURE if the Id hasn't been inited. - */ - nsIPresentationControlChannel connect(in nsITCPDeviceInfo aDeviceInfo); - - /** - * Check the compatibility to remote presentation control server. - * @param aVersion - * The version of remote server. - */ - boolean isCompatibleServer(in uint32_t aVersion); - - /** - * Close server socket and call |listener.onClose(NS_OK)| - */ - void close(); - - /** - * Get the listen port of the TCP socket, valid after the server is ready. - * 0 indicates the server socket is not ready or is closed. - */ - readonly attribute uint16_t port; - - /** - * The protocol version implemented by this server. - */ - readonly attribute uint32_t version; - - /** - * The id of the TCP presentation server. |requestSession| won't - * work until the |id| is set. - */ - attribute AUTF8String id; - - /** - * The fingerprint of the TLS server certificate. - * Empty string indicates the server is not ready or not encrypted. - */ - attribute AUTF8String certFingerprint; - - /** - * The listener for handling events of this presentation control server. - * Listener must be provided before invoke |startServer| and |close|. - */ - attribute nsIPresentationControlServerListener listener; -}; diff --git a/dom/presentation/interfaces/nsIPresentationDevice.idl b/dom/presentation/interfaces/nsIPresentationDevice.idl deleted file mode 100644 index 63e4a52ef..000000000 --- a/dom/presentation/interfaces/nsIPresentationDevice.idl +++ /dev/null @@ -1,43 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationControlChannel; - -/* - * Remote device. - */ -[scriptable, uuid(b1e0a7af-5936-4066-8f2e-f789fb9a7e8f)] -interface nsIPresentationDevice : nsISupports -{ - // The unique Id for the device. UUID is recommanded. - readonly attribute AUTF8String id; - - // The human-readable name of this device. - readonly attribute AUTF8String name; - - // TODO expose more info in order to fulfill UX spec - // The category of this device, could be "wifi", "bluetooth", "hdmi", etc. - readonly attribute AUTF8String type; - - /* - * Establish a control channel to this device. - * @returns The control channel for this session. - * @throws NS_ERROR_FAILURE if the establishment fails - */ - nsIPresentationControlChannel establishControlChannel(); - - // Do something when presentation session is disconnected. - void disconnect(); - - /* - * Query if requested presentation URL is supported. - * @params requestedUrl the designated URL for a presentation request. - * @returns true if designated URL is supported. - */ - boolean isRequestedUrlSupported(in DOMString requestedUrl); -}; - - diff --git a/dom/presentation/interfaces/nsIPresentationDeviceManager.idl b/dom/presentation/interfaces/nsIPresentationDeviceManager.idl deleted file mode 100644 index adff9fc09..000000000 --- a/dom/presentation/interfaces/nsIPresentationDeviceManager.idl +++ /dev/null @@ -1,51 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIArray; -interface nsIPresentationDeviceProvider; - -%{C++ -#define PRESENTATION_DEVICE_MANAGER_CONTRACTID "@mozilla.org/presentation-device/manager;1" -#define PRESENTATION_DEVICE_CHANGE_TOPIC "presentation-device-change" -%} - -/* - * Manager for the device availability. User can observe "presentation-device-change" - * for any update of the available devices. - */ -[scriptable, uuid(beb61db5-3d5f-454f-a15a-dbfa0337c569)] -interface nsIPresentationDeviceManager : nsISupports -{ - // true if there is any device available. - readonly attribute boolean deviceAvailable; - - /* - * Register a device provider manually. - * @param provider The device provider to add. - */ - void addDeviceProvider(in nsIPresentationDeviceProvider provider); - - /* - * Unregister a device provider manually. - * @param provider The device provider to remove. - */ - void removeDeviceProvider(in nsIPresentationDeviceProvider provider); - - /* - * Force all registered device providers to update device information. - */ - void forceDiscovery(); - - /* - * Retrieve all available devices or all available devices that supports - * designated presentation URLs, return a list of nsIPresentationDevice. - * The returned list is a cached device list and could be out-of-date. - * Observe device change events to get following updates. - * @param presentationUrls the target presentation URLs for device filtering - */ - nsIArray getAvailableDevices([optional] in nsIArray presentationUrls); -}; - diff --git a/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl b/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl deleted file mode 100644 index 2900eb59c..000000000 --- a/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl +++ /dev/null @@ -1,58 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIArray; -interface nsIDOMEventTarget; -interface nsIPresentationDevice; -interface nsIPrincipal; - -%{C++ -#define PRESENTATION_DEVICE_PROMPT_CONTRACTID "@mozilla.org/presentation-device/prompt;1" -%} - -/* - * The information and callbacks for device selection - */ -[scriptable, uuid(b2aa7f6a-9448-446a-bba4-9c29638b0ed4)] -interface nsIPresentationDeviceRequest : nsISupports -{ - // The origin which initiate the request. - readonly attribute DOMString origin; - - // The array of candidate URLs. - readonly attribute nsIArray requestURLs; - - // The XUL browser element that the request was originated in. - readonly attribute nsIDOMEventTarget chromeEventHandler; - - // The principal of the request. - readonly attribute nsIPrincipal principal; - - /* - * Callback after selecting a device - * @param device The selected device. - */ - void select(in nsIPresentationDevice device); - - /* - * Callback after selection failed or canceled by user. - * @param reason The error cause for canceling this request. - */ - void cancel(in nsresult reason); -}; - -/* - * UI prompt for device selection. - */ -[scriptable, uuid(ac1a7e44-de86-454f-a9f1-276de2539831)] -interface nsIPresentationDevicePrompt : nsISupports -{ - /* - * Request a device selection. - * @param request The information and callbacks of this selection request. - */ - void promptDeviceSelection(in nsIPresentationDeviceRequest request); -}; diff --git a/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl b/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl deleted file mode 100644 index b2c5e530c..000000000 --- a/dom/presentation/interfaces/nsIPresentationDeviceProvider.idl +++ /dev/null @@ -1,75 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationDevice; -interface nsIPresentationControlChannel; - -%{C++ -#define PRESENTATION_DEVICE_PROVIDER_CATEGORY "presentation-device-provider" -%} - -/* - * The callbacks for any device updates and session request. - */ -[scriptable, uuid(46fd372b-2e40-4179-9b36-0478d141e440)] -interface nsIPresentationDeviceListener: nsISupports -{ - void addDevice(in nsIPresentationDevice device); - void removeDevice(in nsIPresentationDevice device); - void updateDevice(in nsIPresentationDevice device); - - /* - * Callback while the remote device is requesting to start a presentation session. - * @param device The remote device that sent session request. - * @param url The URL requested to open by remote device. - * @param presentationId The Id for representing this session. - * @param controlChannel The control channel for this session. - */ - void onSessionRequest(in nsIPresentationDevice device, - in DOMString url, - in DOMString presentationId, - in nsIPresentationControlChannel controlChannel); - - /* - * Callback while the remote device is requesting to terminate a presentation session. - * @param device The remote device that sent session request. - * @param presentationId The Id for representing this session. - * @param controlChannel The control channel for this session. - * @param aIsFromReceiver true if termination is initiated by receiver. - */ - void onTerminateRequest(in nsIPresentationDevice device, - in DOMString presentationId, - in nsIPresentationControlChannel controlChannel, - in boolean aIsFromReceiver); - - /* - * Callback while the remote device is requesting to reconnect a presentation session. - * @param device The remote device that sent session request. - * @param aUrl The URL requested to open by remote device. - * @param presentationId The Id for representing this session. - * @param controlChannel The control channel for this session. - */ - void onReconnectRequest(in nsIPresentationDevice device, - in DOMString url, - in DOMString presentationId, - in nsIPresentationControlChannel controlChannel); -}; - -/* - * Device provider for any device protocol, can be registered as default - * providers by adding its contractID to category "presentation-device-provider". - */ -[scriptable, uuid(3db2578a-0f50-44ad-b01b-28427b71b7bf)] -interface nsIPresentationDeviceProvider: nsISupports -{ - // The listener for handling any device update. - attribute nsIPresentationDeviceListener listener; - - /* - * Force to update device information. - */ - void forceDiscovery(); -}; diff --git a/dom/presentation/interfaces/nsIPresentationListener.idl b/dom/presentation/interfaces/nsIPresentationListener.idl deleted file mode 100644 index 546c2fd4b..000000000 --- a/dom/presentation/interfaces/nsIPresentationListener.idl +++ /dev/null @@ -1,50 +0,0 @@ -/* 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 "nsISupports.idl" - -[ref] native URLArrayRef(const nsTArray<nsString>); - -[uuid(0105f837-4279-4715-9d5b-2dc3f8b65353)] -interface nsIPresentationAvailabilityListener : nsISupports -{ - /* - * Called when device availability changes. - */ - [noscript] void notifyAvailableChange(in URLArrayRef urls, - in bool available); -}; - -[scriptable, uuid(7dd48df8-8f8c-48c7-ac37-7b9fd1acf2f8)] -interface nsIPresentationSessionListener : nsISupports -{ - const unsigned short STATE_CONNECTING = 0; - const unsigned short STATE_CONNECTED = 1; - const unsigned short STATE_CLOSED = 2; - const unsigned short STATE_TERMINATED = 3; - - /* - * Called when session state changes. - */ - void notifyStateChange(in DOMString sessionId, - in unsigned short state, - in nsresult reason); - - /* - * Called when receive messages. - */ - void notifyMessage(in DOMString sessionId, - in ACString data, - in boolean isBinary); -}; - -[scriptable, uuid(27f101d7-9ed1-429e-b4f8-43b00e8e111c)] -interface nsIPresentationRespondingListener : nsISupports -{ - /* - * Called when an incoming session connects. - */ - void notifySessionConnect(in unsigned long long windowId, - in DOMString sessionId); -}; diff --git a/dom/presentation/interfaces/nsIPresentationLocalDevice.idl b/dom/presentation/interfaces/nsIPresentationLocalDevice.idl deleted file mode 100644 index 80e3b4041..000000000 --- a/dom/presentation/interfaces/nsIPresentationLocalDevice.idl +++ /dev/null @@ -1,17 +0,0 @@ -/* 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 "nsIPresentationDevice.idl" - -/* - * Local device. - * This device is used for 1-UA use case. The result for display is rendered by - * this host device. - */ -[scriptable, uuid(dd239720-cab6-4fb5-9025-cba23f1bbc2d)] -interface nsIPresentationLocalDevice : nsIPresentationDevice -{ - // (1-UA only) The property is used to get the window ID of 1-UA device. - readonly attribute AUTF8String windowId; -}; diff --git a/dom/presentation/interfaces/nsIPresentationNetworkHelper.idl b/dom/presentation/interfaces/nsIPresentationNetworkHelper.idl deleted file mode 100644 index 514075dfa..000000000 --- a/dom/presentation/interfaces/nsIPresentationNetworkHelper.idl +++ /dev/null @@ -1,36 +0,0 @@ -/* 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 "nsISupports.idl" - -%{C++ -#define PRESENTATION_NETWORK_HELPER_CONTRACTID \ - "@mozilla.org/presentation-device/networkHelper;1" -%} - -[scriptable, uuid(0a7e134f-ff80-4e73-91e6-12b3134fe568)] -interface nsIPresentationNetworkHelperListener : nsISupports -{ - /** - * Called when error occurs. - * @param aReason error message. - */ - void onError(in AUTF8String aReason); - - /** - * Called when get Wi-Fi IP address. - * @param aIPAddress the IP address of Wi-Fi interface. - */ - void onGetWifiIPAddress(in AUTF8String aIPAddress); -}; - -[scriptable, uuid(650dc16b-3d9c-49a6-9037-1d6f2d18c90c)] -interface nsIPresentationNetworkHelper : nsISupports -{ - /** - * Get IP address of Wi-Fi interface. - * @param aListener the callback interface. - */ - void getWifiIPAddress(in nsIPresentationNetworkHelperListener aListener); -}; diff --git a/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl b/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl deleted file mode 100644 index dab1991e4..000000000 --- a/dom/presentation/interfaces/nsIPresentationRequestUIGlue.idl +++ /dev/null @@ -1,29 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationDevice; - -%{C++ -#define PRESENTATION_REQUEST_UI_GLUE_CONTRACTID \ - "@mozilla.org/presentation/requestuiglue;1" -%} - -[scriptable, uuid(faa45119-6fb5-496c-aa4c-f740177a38b5)] -interface nsIPresentationRequestUIGlue : nsISupports -{ - /* - * This method is called to open the responding app/page when - * a presentation request comes in at receiver side. - * - * @param url The url of the request. - * @param sessionId The session ID of the request. - * - * @return A promise that resolves to the opening frame. - */ - nsISupports sendRequest(in DOMString url, - in DOMString sessionId, - in nsIPresentationDevice device); -}; diff --git a/dom/presentation/interfaces/nsIPresentationService.idl b/dom/presentation/interfaces/nsIPresentationService.idl deleted file mode 100644 index c3c15bb9f..000000000 --- a/dom/presentation/interfaces/nsIPresentationService.idl +++ /dev/null @@ -1,275 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIDOMBlob; -interface nsIDOMEventTarget; -interface nsIInputStream; -interface nsIPresentationAvailabilityListener; -interface nsIPresentationRespondingListener; -interface nsIPresentationSessionListener; -interface nsIPresentationTransportBuilderConstructor; -interface nsIPrincipal; - -%{C++ -#define PRESENTATION_SERVICE_CID \ - { 0x1d9bb10c, 0xc0ab, 0x4fe8, \ - { 0x9e, 0x4f, 0x40, 0x58, 0xb8, 0x51, 0x98, 0x32 } } -#define PRESENTATION_SERVICE_CONTRACTID \ - "@mozilla.org/presentation/presentationservice;1" - -#include "nsTArray.h" - -class nsString; -%} - -[ref] native URLArrayRef(const nsTArray<nsString>); - -[scriptable, uuid(12073206-0065-4b10-9488-a6eb9b23e65b)] -interface nsIPresentationServiceCallback : nsISupports -{ - /* - * Called when the operation succeeds. - * - * @param url: the selected request url used to start or reconnect a session. - */ - void notifySuccess(in DOMString url); - - /* - * Called when the operation fails. - * - * @param error: error message. - */ - void notifyError(in nsresult error); -}; - -[scriptable, uuid(de42b741-5619-4650-b961-c2cebb572c95)] -interface nsIPresentationService : nsISupports -{ - const unsigned short ROLE_CONTROLLER = 0x1; - const unsigned short ROLE_RECEIVER = 0x2; - - const unsigned short CLOSED_REASON_ERROR = 0x1; - const unsigned short CLOSED_REASON_CLOSED = 0x2; - const unsigned short CLOSED_REASON_WENTAWAY = 0x3; - - /* - * Start a new presentation session and display a prompt box which asks users - * to select a device. - * - * @param urls: The candidate Urls of presenting page. Only one url would be used. - * @param sessionId: An ID to identify presentation session. - * @param origin: The url of requesting page. - * @param deviceId: The specified device of handling this request, null string - * for prompt device selection dialog. - * @param windowId: The inner window ID associated with the presentation - * session. (0 implies no window ID since no actual window - * uses 0 as its ID. Generally it's the case the window is - * located in different process from this service) - * @param eventTarget: The chrome event handler, in particular XUL browser - * element in parent process, that the request was - * originated in. - * @param principal: The principal that initiated the session. - * @param callback: Invoke the callback when the operation is completed. - * NotifySuccess() is called with |id| if a session is - * established successfully with the selected device. - * Otherwise, NotifyError() is called with a error message. - * @param constructor: The constructor for creating a transport builder. - */ - [noscript] void startSession(in URLArrayRef urls, - in DOMString sessionId, - in DOMString origin, - in DOMString deviceId, - in unsigned long long windowId, - in nsIDOMEventTarget eventTarget, - in nsIPrincipal principal, - in nsIPresentationServiceCallback callback, - in nsIPresentationTransportBuilderConstructor constructor); - - /* - * Send the message to the session. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param data: the message being sent out. - */ - void sendSessionMessage(in DOMString sessionId, - in uint8_t role, - in DOMString data); - - /* - * Send the binary message to the session. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param data: the message being sent out. - */ - void sendSessionBinaryMsg(in DOMString sessionId, - in uint8_t role, - in ACString data); - - /* - * Send the blob to the session. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param blob: The input blob to be sent. - */ - void sendSessionBlob(in DOMString sessionId, - in uint8_t role, - in nsIDOMBlob blob); - - /* - * Close the session. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - void closeSession(in DOMString sessionId, - in uint8_t role, - in uint8_t closedReason); - - /* - * Terminate the session. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - void terminateSession(in DOMString sessionId, - in uint8_t role); - - /* - * Reconnect the session. - * - * @param url: The request Urls. - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param callback: NotifySuccess() is called when a control channel - * is opened successfully. - * Otherwise, NotifyError() is called with a error message. - */ - [noscript] void reconnectSession(in URLArrayRef urls, - in DOMString sessionId, - in uint8_t role, - in nsIPresentationServiceCallback callback); - - /* - * Register an availability listener. Must be called from the main thread. - * - * @param availabilityUrls: The Urls that this listener is interested in. - * @param listener: The listener to register. - */ - [noscript] void registerAvailabilityListener( - in URLArrayRef availabilityUrls, - in nsIPresentationAvailabilityListener listener); - - /* - * Unregister an availability listener. Must be called from the main thread. - * - * @param availabilityUrls: The Urls that are registered before. - * @param listener: The listener to unregister. - */ - [noscript] void unregisterAvailabilityListener( - in URLArrayRef availabilityUrls, - in nsIPresentationAvailabilityListener listener); - - /* - * Register a session listener. Must be called from the main thread. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param listener: The listener to register. - */ - void registerSessionListener(in DOMString sessionId, - in uint8_t role, - in nsIPresentationSessionListener listener); - - /* - * Unregister a session listener. Must be called from the main thread. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - void unregisterSessionListener(in DOMString sessionId, - in uint8_t role); - - /* - * Register a responding listener. Must be called from the main thread. - * - * @param windowId: The window ID associated with the listener. - * @param listener: The listener to register. - */ - void registerRespondingListener(in unsigned long long windowId, - in nsIPresentationRespondingListener listener); - - /* - * Unregister a responding listener. Must be called from the main thread. - * @param windowId: The window ID associated with the listener. - */ - void unregisterRespondingListener(in unsigned long long windowId); - - /* - * Notify the receiver page is ready for presentation use. - * - * @param sessionId An ID to identify presentation session. - * @param windowId The inner window ID associated with the presentation - * session. - * @param isLoading true if receiver page is loading successfully. - * @param constructor: The constructor for creating a transport builder. - */ - void notifyReceiverReady(in DOMString sessionId, - in unsigned long long windowId, - in boolean isLoading, - in nsIPresentationTransportBuilderConstructor constructor); - - /* - * Notify the transport is closed - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param reason: the error message. NS_OK indicates it is closed normally. - */ - void NotifyTransportClosed(in DOMString sessionId, - in uint8_t role, - in nsresult reason); - - /* - * Untrack the relevant info about the presentation session if there's any. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - void untrackSessionInfo(in DOMString sessionId, in uint8_t role); - - /* - * The windowId for building RTCDataChannel session transport - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - unsigned long long getWindowIdBySessionId(in DOMString sessionId, - in uint8_t role); - - /* - * Update the mapping of the session ID and window ID. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - * @param windowId: The inner window ID associated with the presentation - * session. - */ - void updateWindowIdBySessionId(in DOMString sessionId, - in uint8_t role, - in unsigned long long windowId); - - /* - * To build the session transport. - * NOTE: This function should be only called at controller side. - * - * @param sessionId: An ID to identify presentation session. - * @param role: Identify the function called by controller or receiver. - */ - void buildTransport(in DOMString sessionId, in uint8_t role); -}; diff --git a/dom/presentation/interfaces/nsIPresentationSessionRequest.idl b/dom/presentation/interfaces/nsIPresentationSessionRequest.idl deleted file mode 100644 index 45a0e314c..000000000 --- a/dom/presentation/interfaces/nsIPresentationSessionRequest.idl +++ /dev/null @@ -1,35 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationDevice; -interface nsIPresentationControlChannel; - -%{C++ -#define PRESENTATION_SESSION_REQUEST_TOPIC "presentation-session-request" -#define PRESENTATION_RECONNECT_REQUEST_TOPIC "presentation-reconnect-request" -%} - -/* - * The event of a device requesting for starting or reconnecting - * a presentation session. User can monitor the session request - * on every device by observing "presentation-sesion-request" for a - * new session and "presentation-reconnect-request" for reconnecting. - */ -[scriptable, uuid(d808a084-d0f8-455a-a8df-5879e05a755b)] -interface nsIPresentationSessionRequest: nsISupports -{ - // The device which requesting the presentation session. - readonly attribute nsIPresentationDevice device; - - // The URL requested to open by remote device. - readonly attribute DOMString url; - - // The Id for representing this session. - readonly attribute DOMString presentationId; - - // The control channel for this session. - readonly attribute nsIPresentationControlChannel controlChannel; -}; diff --git a/dom/presentation/interfaces/nsIPresentationSessionTransport.idl b/dom/presentation/interfaces/nsIPresentationSessionTransport.idl deleted file mode 100644 index a0b5617d7..000000000 --- a/dom/presentation/interfaces/nsIPresentationSessionTransport.idl +++ /dev/null @@ -1,69 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIDOMBlob; -interface nsIInputStream; -interface nsINetAddr; - -%{C++ -#define PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID \ - "@mozilla.org/presentation/presentationtcpsessiontransport;1" -%} - -/* - * The callback for session transport events. - */ -[scriptable, uuid(9f158786-41a6-4a10-b29b-9497f25d4b67)] -interface nsIPresentationSessionTransportCallback : nsISupports -{ - void notifyTransportReady(); - void notifyTransportClosed(in nsresult reason); - void notifyData(in ACString data, in boolean isBinary); -}; - -/* - * App-to-App transport channel for the presentation session. - */ -[scriptable, uuid(670b7e1b-65be-42b6-a596-be571907fa18)] -interface nsIPresentationSessionTransport : nsISupports -{ - // Should be set once the underlying session transport is built - attribute nsIPresentationSessionTransportCallback callback; - - // valid for TCP session transport - readonly attribute nsINetAddr selfAddress; - - /* - * Enable the notification for incoming data. |notifyData| of - * |nsIPresentationSessionTransportCallback| can start getting invoked. - * Should set callback before |enableDataNotification| is called. - */ - void enableDataNotification(); - - /* - * Send message to the remote endpoint. - * @param data The message to send. - */ - void send(in DOMString data); - - /* - * Send the binary message to the remote endpoint. - * @param data: the message being sent out. - */ - void sendBinaryMsg(in ACString data); - - /* - * Send the blob to the remote endpoint. - * @param blob: The input blob to be sent. - */ - void sendBlob(in nsIDOMBlob blob); - - /* - * Close this session transport. - * @param reason The reason for closing this session transport. - */ - void close(in nsresult reason); -}; diff --git a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl b/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl deleted file mode 100644 index 969f37d71..000000000 --- a/dom/presentation/interfaces/nsIPresentationSessionTransportBuilder.idl +++ /dev/null @@ -1,80 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationChannelDescription; -interface nsISocketTransport; -interface mozIDOMWindow; -interface nsIPresentationControlChannel; -interface nsIPresentationSessionTransport; - -[scriptable, uuid(673f6de1-e253-41b8-9be8-b7ff161fa8dc)] -interface nsIPresentationSessionTransportBuilderListener : nsISupports -{ - // Should set |transport.callback| in |onSessionTransport|. - void onSessionTransport(in nsIPresentationSessionTransport transport); - void onError(in nsresult reason); - - void sendOffer(in nsIPresentationChannelDescription offer); - void sendAnswer(in nsIPresentationChannelDescription answer); - void sendIceCandidate(in DOMString candidate); - void close(in nsresult reason); -}; - -[scriptable, uuid(2fdbe67d-80f9-48dc-8237-5bef8fa19801)] -interface nsIPresentationSessionTransportBuilder : nsISupports -{ -}; - -/** - * The constructor for creating a transport builder. - */ -[scriptable, uuid(706482b2-1b51-4bed-a21d-785a9cfcfac7)] -interface nsIPresentationTransportBuilderConstructor : nsISupports -{ - nsIPresentationSessionTransportBuilder createTransportBuilder(in uint8_t type); -}; - -/** - * Builder for TCP session transport - */ -[scriptable, uuid(cde36d6e-f471-4262-a70d-f932a26b21d9)] -interface nsIPresentationTCPSessionTransportBuilder : nsIPresentationSessionTransportBuilder -{ - /** - * The following creation functions will trigger |listener.onSessionTransport| - * if the session transport is successfully built, |listener.onError| if some - * error occurs during building session transport. - */ - void buildTCPSenderTransport(in nsISocketTransport aTransport, - in nsIPresentationSessionTransportBuilderListener aListener); - - void buildTCPReceiverTransport(in nsIPresentationChannelDescription aDescription, - in nsIPresentationSessionTransportBuilderListener aListener); -}; - -/** - * Builder for WebRTC data channel session transport - */ -[scriptable, uuid(8131c4e0-3a8c-4bc1-a92a-8431473d2fe8)] -interface nsIPresentationDataChannelSessionTransportBuilder : nsIPresentationSessionTransportBuilder -{ - /** - * The following creation function will trigger |listener.onSessionTransport| - * if the session transport is successfully built, |listener.onError| if some - * error occurs during creating session transport. The |notifyConnected| of - * |aControlChannel| should be called before calling - * |buildDataChannelTransport|. - */ - void buildDataChannelTransport(in uint8_t aRole, - in mozIDOMWindow aWindow, - in nsIPresentationSessionTransportBuilderListener aListener); - - // Bug 1275150 - Merge TCP builder with the following APIs - void onOffer(in nsIPresentationChannelDescription offer); - void onAnswer(in nsIPresentationChannelDescription answer); - void onIceCandidate(in DOMString candidate); - void notifyDisconnected(in nsresult reason); -}; diff --git a/dom/presentation/interfaces/nsIPresentationTerminateRequest.idl b/dom/presentation/interfaces/nsIPresentationTerminateRequest.idl deleted file mode 100644 index a9f86fa0d..000000000 --- a/dom/presentation/interfaces/nsIPresentationTerminateRequest.idl +++ /dev/null @@ -1,33 +0,0 @@ -/* 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 "nsISupports.idl" - -interface nsIPresentationDevice; -interface nsIPresentationControlChannel; - -%{C++ -#define PRESENTATION_TERMINATE_REQUEST_TOPIC "presentation-terminate-request" -%} - -/* - * The event of a device requesting for terminating a presentation session. User can - * monitor the terminate request on every device by observing "presentation-terminate-request". - */ -[scriptable, uuid(3ddbf3a4-53ee-4b70-9bbc-58ac90dce6b5)] -interface nsIPresentationTerminateRequest: nsISupports -{ - // The device which requesting to terminate presentation session. - readonly attribute nsIPresentationDevice device; - - // The Id for representing this session. - readonly attribute DOMString presentationId; - - // The control channel for this session. - // Should only use this channel to complete session termination. - readonly attribute nsIPresentationControlChannel controlChannel; - - // True if termination is initiated by receiver. - readonly attribute boolean isFromReceiver; -}; diff --git a/dom/presentation/ipc/PPresentation.ipdl b/dom/presentation/ipc/PPresentation.ipdl deleted file mode 100644 index e0f4d2888..000000000 --- a/dom/presentation/ipc/PPresentation.ipdl +++ /dev/null @@ -1,112 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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 protocol PContent; -include protocol PPresentationRequest; -include protocol PPresentationBuilder; - -include InputStreamParams; - -using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; -using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; - -namespace mozilla { -namespace dom { - -struct StartSessionRequest -{ - nsString[] urls; - nsString sessionId; - nsString origin; - nsString deviceId; - uint64_t windowId; - TabId tabId; - Principal principal; -}; - -struct SendSessionMessageRequest -{ - nsString sessionId; - uint8_t role; - nsString data; -}; - -struct CloseSessionRequest -{ - nsString sessionId; - uint8_t role; - uint8_t closedReason; -}; - -struct TerminateSessionRequest -{ - nsString sessionId; - uint8_t role; -}; - -struct ReconnectSessionRequest -{ - nsString[] urls; - nsString sessionId; - uint8_t role; -}; - -struct BuildTransportRequest -{ - nsString sessionId; - uint8_t role; -}; - -union PresentationIPCRequest -{ - StartSessionRequest; - SendSessionMessageRequest; - CloseSessionRequest; - TerminateSessionRequest; - ReconnectSessionRequest; - BuildTransportRequest; -}; - -sync protocol PPresentation -{ - manager PContent; - manages PPresentationBuilder; - manages PPresentationRequest; - -child: - async NotifyAvailableChange(nsString[] aAvailabilityUrls, - bool aAvailable); - async NotifySessionStateChange(nsString aSessionId, - uint16_t aState, - nsresult aReason); - async NotifyMessage(nsString aSessionId, nsCString aData, bool aIsBinary); - async NotifySessionConnect(uint64_t aWindowId, nsString aSessionId); - async NotifyCloseSessionTransport(nsString aSessionId, - uint8_t aRole, - nsresult aReason); - - async PPresentationBuilder(nsString aSessionId, uint8_t aRole); - -parent: - async __delete__(); - - async RegisterAvailabilityHandler(nsString[] aAvailabilityUrls); - async UnregisterAvailabilityHandler(nsString[] aAvailabilityUrls); - - async RegisterSessionHandler(nsString aSessionId, uint8_t aRole); - async UnregisterSessionHandler(nsString aSessionId, uint8_t aRole); - - async RegisterRespondingHandler(uint64_t aWindowId); - async UnregisterRespondingHandler(uint64_t aWindowId); - - async PPresentationRequest(PresentationIPCRequest aRequest); - - async NotifyReceiverReady(nsString aSessionId, uint64_t aWindowId, bool aIsLoading); - async NotifyTransportClosed(nsString aSessionId, uint8_t aRole, nsresult aReason); -}; - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PPresentationBuilder.ipdl b/dom/presentation/ipc/PPresentationBuilder.ipdl deleted file mode 100644 index e32b02e8f..000000000 --- a/dom/presentation/ipc/PPresentationBuilder.ipdl +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 protocol PPresentation; - -namespace mozilla { -namespace dom { - -async protocol PPresentationBuilder -{ - manager PPresentation; - -parent: - async SendOffer(nsString aSDP); - async SendAnswer(nsString aSDP); - async SendIceCandidate(nsString aCandidate); - async Close(nsresult aReason); - - async OnSessionTransport(); - async OnSessionTransportError(nsresult aReason); - -child: - async OnOffer(nsString aSDP); - async OnAnswer(nsString aSDP); - async OnIceCandidate(nsString aCandidate); - - async __delete__(); -}; - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PPresentationRequest.ipdl b/dom/presentation/ipc/PPresentationRequest.ipdl deleted file mode 100644 index fa99dfcab..000000000 --- a/dom/presentation/ipc/PPresentationRequest.ipdl +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 protocol PPresentation; - -namespace mozilla { -namespace dom { - -sync protocol PPresentationRequest -{ - manager PPresentation; - -child: - async __delete__(nsresult result); - async NotifyRequestUrlSelected(nsString aUrl); -}; - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationBuilderChild.cpp b/dom/presentation/ipc/PresentationBuilderChild.cpp deleted file mode 100644 index 387332e9e..000000000 --- a/dom/presentation/ipc/PresentationBuilderChild.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 "DCPresentationChannelDescription.h" -#include "nsComponentManagerUtils.h" -#include "nsGlobalWindow.h" -#include "PresentationBuilderChild.h" -#include "PresentationIPCService.h" -#include "nsServiceManagerUtils.h" -#include "mozilla/Unused.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(PresentationBuilderChild, - nsIPresentationSessionTransportBuilderListener) - -PresentationBuilderChild::PresentationBuilderChild(const nsString& aSessionId, - uint8_t aRole) - : mSessionId(aSessionId) - , mRole(aRole) -{ -} - -nsresult PresentationBuilderChild::Init() -{ - mBuilder = do_CreateInstance("@mozilla.org/presentation/datachanneltransportbuilder;1"); - if (NS_WARN_IF(!mBuilder)) { - return NS_ERROR_NOT_AVAILABLE; - } - - uint64_t windowId = 0; - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (NS_WARN_IF(NS_FAILED(service->GetWindowIdBySessionId( - mSessionId, - mRole, - &windowId)))) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsPIDOMWindowInner* window = nsGlobalWindow::GetInnerWindowWithId(windowId)->AsInner(); - if (NS_WARN_IF(!window)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mBuilder->BuildDataChannelTransport(mRole, window, this); -} - -void -PresentationBuilderChild::ActorDestroy(ActorDestroyReason aWhy) -{ - mBuilder = nullptr; - mActorDestroyed = true; -} - -bool -PresentationBuilderChild::RecvOnOffer(const nsString& aSDP) -{ - if (NS_WARN_IF(!mBuilder)) { - return false; - } - RefPtr<DCPresentationChannelDescription> description = - new DCPresentationChannelDescription(aSDP); - - if (NS_WARN_IF(NS_FAILED(mBuilder->OnOffer(description)))) { - return false; - } - return true; -} - -bool -PresentationBuilderChild::RecvOnAnswer(const nsString& aSDP) -{ - if (NS_WARN_IF(!mBuilder)) { - return false; - } - RefPtr<DCPresentationChannelDescription> description = - new DCPresentationChannelDescription(aSDP); - - if (NS_WARN_IF(NS_FAILED(mBuilder->OnAnswer(description)))) { - return false; - } - return true; -} - -bool -PresentationBuilderChild::RecvOnIceCandidate(const nsString& aCandidate) -{ - if (NS_WARN_IF(mBuilder && NS_FAILED(mBuilder->OnIceCandidate(aCandidate)))) { - return false; - } - return true; -} - -// nsPresentationSessionTransportBuilderListener -NS_IMETHODIMP -PresentationBuilderChild::OnSessionTransport(nsIPresentationSessionTransport* aTransport) -{ - if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransport())){ - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - NS_WARNING_ASSERTION(service, "no presentation service"); - if (service) { - Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())-> - NotifySessionTransport(mSessionId, mRole, aTransport))); - } - mBuilder = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderChild::OnError(nsresult reason) -{ - mBuilder = nullptr; - - if (NS_WARN_IF(mActorDestroyed || !SendOnSessionTransportError(reason))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderChild::SendOffer(nsIPresentationChannelDescription* aOffer) -{ - nsAutoString SDP; - nsresult rv = aOffer->GetDataChannelSDP(SDP); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(mActorDestroyed || !SendSendOffer(SDP))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderChild::SendAnswer(nsIPresentationChannelDescription* aAnswer) -{ - nsAutoString SDP; - nsresult rv = aAnswer->GetDataChannelSDP(SDP); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(mActorDestroyed || !SendSendAnswer(SDP))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderChild::SendIceCandidate(const nsAString& candidate) -{ - if (NS_WARN_IF(mActorDestroyed || !SendSendIceCandidate(nsString(candidate)))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderChild::Close(nsresult reason) -{ - if (NS_WARN_IF(mActorDestroyed || !SendClose(reason))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -} // namespace dom -} // namespace mozilla - diff --git a/dom/presentation/ipc/PresentationBuilderChild.h b/dom/presentation/ipc/PresentationBuilderChild.h deleted file mode 100644 index 5ada53ab7..000000000 --- a/dom/presentation/ipc/PresentationBuilderChild.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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_PresentationBuilderChild_h -#define mozilla_dom_PresentationBuilderChild_h - -#include "mozilla/dom/PPresentationBuilderChild.h" -#include "nsIPresentationSessionTransportBuilder.h" - -namespace mozilla { -namespace dom { - -class PresentationBuilderChild final: public PPresentationBuilderChild - , public nsIPresentationSessionTransportBuilderListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDERLISTENER - - explicit PresentationBuilderChild(const nsString& aSessionId, - uint8_t aRole); - - nsresult Init(); - - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - - virtual bool RecvOnOffer(const nsString& aSDP) override; - - virtual bool RecvOnAnswer(const nsString& aSDP) override; - - virtual bool RecvOnIceCandidate(const nsString& aCandidate) override; - -private: - virtual ~PresentationBuilderChild() = default; - - nsString mSessionId; - uint8_t mRole; - bool mActorDestroyed = false; - nsCOMPtr<nsIPresentationDataChannelSessionTransportBuilder> mBuilder; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationBuilderChild_h diff --git a/dom/presentation/ipc/PresentationBuilderParent.cpp b/dom/presentation/ipc/PresentationBuilderParent.cpp deleted file mode 100644 index dc784b13c..000000000 --- a/dom/presentation/ipc/PresentationBuilderParent.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 "DCPresentationChannelDescription.h" -#include "PresentationBuilderParent.h" -#include "PresentationSessionInfo.h" - -namespace mozilla { -namespace dom { - -namespace { - -class PresentationSessionTransportIPC final : - public nsIPresentationSessionTransport -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORT - - PresentationSessionTransportIPC(PresentationParent* aParent, - const nsAString& aSessionId, - uint8_t aRole) - : mParent(aParent) - , mSessionId(aSessionId) - , mRole(aRole) - { - MOZ_ASSERT(mParent); - } - -private: - virtual ~PresentationSessionTransportIPC() = default; - - RefPtr<PresentationParent> mParent; - nsString mSessionId; - uint8_t mRole; -}; - -NS_IMPL_ISUPPORTS(PresentationSessionTransportIPC, - nsIPresentationSessionTransport) - -NS_IMETHODIMP -PresentationSessionTransportIPC::GetCallback( - nsIPresentationSessionTransportCallback** aCallback) -{ - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::SetCallback( - nsIPresentationSessionTransportCallback* aCallback) -{ - if (aCallback) { - aCallback->NotifyTransportReady(); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::GetSelfAddress(nsINetAddr** aSelfAddress) -{ - MOZ_ASSERT(false, "Not expected."); - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::EnableDataNotification() -{ - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::Send(const nsAString& aData) -{ - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::SendBinaryMsg(const nsACString& aData) -{ - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::SendBlob(nsIDOMBlob* aBlob) -{ - return NS_OK; -} - -NS_IMETHODIMP -PresentationSessionTransportIPC::Close(nsresult aReason) -{ - if (NS_WARN_IF(!mParent->SendNotifyCloseSessionTransport(mSessionId, - mRole, - aReason))) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -} // anonymous namespace - -NS_IMPL_ISUPPORTS(PresentationBuilderParent, - nsIPresentationSessionTransportBuilder, - nsIPresentationDataChannelSessionTransportBuilder) - -PresentationBuilderParent::PresentationBuilderParent(PresentationParent* aParent) - : mParent(aParent) -{ - MOZ_COUNT_CTOR(PresentationBuilderParent); -} - -PresentationBuilderParent::~PresentationBuilderParent() -{ - MOZ_COUNT_DTOR(PresentationBuilderParent); - - if (mNeedDestroyActor) { - Unused << NS_WARN_IF(!Send__delete__(this)); - } -} - -NS_IMETHODIMP -PresentationBuilderParent::BuildDataChannelTransport( - uint8_t aRole, - mozIDOMWindow* aWindow, /* unused */ - nsIPresentationSessionTransportBuilderListener* aListener) -{ - mBuilderListener = aListener; - - RefPtr<PresentationSessionInfo> info = static_cast<PresentationSessionInfo*>(aListener); - nsAutoString sessionId(info->GetSessionId()); - if (NS_WARN_IF(!mParent->SendPPresentationBuilderConstructor(this, - sessionId, - aRole))) { - return NS_ERROR_FAILURE; - } - mIPCSessionTransport = new PresentationSessionTransportIPC(mParent, - sessionId, - aRole); - mNeedDestroyActor = true; - mParent = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderParent::OnIceCandidate(const nsAString& aCandidate) -{ - if (NS_WARN_IF(!SendOnIceCandidate(nsString(aCandidate)))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderParent::OnOffer(nsIPresentationChannelDescription* aDescription) -{ - nsAutoString SDP; - nsresult rv = aDescription->GetDataChannelSDP(SDP); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!SendOnOffer(SDP))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderParent::OnAnswer(nsIPresentationChannelDescription* aDescription) -{ - nsAutoString SDP; - nsresult rv = aDescription->GetDataChannelSDP(SDP); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!SendOnAnswer(SDP))){ - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationBuilderParent::NotifyDisconnected(nsresult aReason) -{ - return NS_OK; -} - -void -PresentationBuilderParent::ActorDestroy(ActorDestroyReason aWhy) -{ - mNeedDestroyActor = false; - mParent = nullptr; - mBuilderListener = nullptr; -} - -bool -PresentationBuilderParent::RecvSendOffer(const nsString& aSDP) -{ - RefPtr<DCPresentationChannelDescription> description = - new DCPresentationChannelDescription(aSDP); - if (NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->SendOffer(description)))) { - return false; - } - return true; -} - -bool -PresentationBuilderParent::RecvSendAnswer(const nsString& aSDP) -{ - RefPtr<DCPresentationChannelDescription> description = - new DCPresentationChannelDescription(aSDP); - if (NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->SendAnswer(description)))) { - return false; - } - return true; -} - -bool -PresentationBuilderParent::RecvSendIceCandidate(const nsString& aCandidate) -{ - if (NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->SendIceCandidate(aCandidate)))) { - return false; - } - return true; -} - -bool -PresentationBuilderParent::RecvClose(const nsresult& aReason) -{ - if (NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->Close(aReason)))) { - return false; - } - return true; -} - -// Delegate to nsIPresentationSessionTransportBuilderListener -bool -PresentationBuilderParent::RecvOnSessionTransport() -{ - RefPtr<PresentationBuilderParent> kungFuDeathGrip = this; - Unused << - NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->OnSessionTransport(mIPCSessionTransport))); - return true; -} - -bool -PresentationBuilderParent::RecvOnSessionTransportError(const nsresult& aReason) -{ - if (NS_WARN_IF(!mBuilderListener || - NS_FAILED(mBuilderListener->OnError(aReason)))) { - return false; - } - return true; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationBuilderParent.h b/dom/presentation/ipc/PresentationBuilderParent.h deleted file mode 100644 index 7fd211ce5..000000000 --- a/dom/presentation/ipc/PresentationBuilderParent.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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_PresentationBuilderParent_h__ -#define mozilla_dom_PresentationBuilderParent_h__ - -#include "mozilla/dom/PPresentationBuilderParent.h" -#include "PresentationParent.h" -#include "nsIPresentationSessionTransportBuilder.h" - -namespace mozilla { -namespace dom { - -class PresentationBuilderParent final: public PPresentationBuilderParent - , public nsIPresentationDataChannelSessionTransportBuilder -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTBUILDER - NS_DECL_NSIPRESENTATIONDATACHANNELSESSIONTRANSPORTBUILDER - - explicit PresentationBuilderParent(PresentationParent* aParent); - - virtual bool RecvSendOffer(const nsString& aSDP) override; - - virtual bool RecvSendAnswer(const nsString& aSDP) override; - - virtual bool RecvSendIceCandidate(const nsString& aCandidate) override; - - virtual bool RecvClose(const nsresult& aReason) override; - - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - - virtual bool RecvOnSessionTransport() override; - - virtual bool RecvOnSessionTransportError(const nsresult& aReason) override; - -private: - virtual ~PresentationBuilderParent(); - bool mNeedDestroyActor = false; - RefPtr<PresentationParent> mParent; - nsCOMPtr<nsIPresentationSessionTransportBuilderListener> mBuilderListener; - nsCOMPtr<nsIPresentationSessionTransport> mIPCSessionTransport; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationBuilderParent_h__ diff --git a/dom/presentation/ipc/PresentationChild.cpp b/dom/presentation/ipc/PresentationChild.cpp deleted file mode 100644 index d24ba9e8c..000000000 --- a/dom/presentation/ipc/PresentationChild.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 "DCPresentationChannelDescription.h" -#include "mozilla/StaticPtr.h" -#include "PresentationBuilderChild.h" -#include "PresentationChild.h" -#include "PresentationIPCService.h" -#include "nsThreadUtils.h" - -using namespace mozilla; -using namespace mozilla::dom; - -/* - * Implementation of PresentationChild - */ - -PresentationChild::PresentationChild(PresentationIPCService* aService) - : mActorDestroyed(false) - , mService(aService) -{ - MOZ_ASSERT(mService); - - MOZ_COUNT_CTOR(PresentationChild); -} - -PresentationChild::~PresentationChild() -{ - MOZ_COUNT_DTOR(PresentationChild); - - if (!mActorDestroyed) { - Send__delete__(this); - } - mService = nullptr; -} - -void -PresentationChild::ActorDestroy(ActorDestroyReason aWhy) -{ - mActorDestroyed = true; - mService->NotifyPresentationChildDestroyed(); - mService = nullptr; -} - -PPresentationRequestChild* -PresentationChild::AllocPPresentationRequestChild(const PresentationIPCRequest& aRequest) -{ - NS_NOTREACHED("We should never be manually allocating PPresentationRequestChild actors"); - return nullptr; -} - -bool -PresentationChild::DeallocPPresentationRequestChild(PPresentationRequestChild* aActor) -{ - delete aActor; - return true; -} - -bool PresentationChild::RecvPPresentationBuilderConstructor( - PPresentationBuilderChild* aActor, - const nsString& aSessionId, - const uint8_t& aRole) -{ - // Child will build the session transport - PresentationBuilderChild* actor = static_cast<PresentationBuilderChild*>(aActor); - return NS_WARN_IF(NS_FAILED(actor->Init())) ? false : true; -} - -PPresentationBuilderChild* -PresentationChild::AllocPPresentationBuilderChild(const nsString& aSessionId, - const uint8_t& aRole) -{ - RefPtr<PresentationBuilderChild> actor - = new PresentationBuilderChild(aSessionId, aRole); - - return actor.forget().take(); -} - -bool -PresentationChild::DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor) -{ - RefPtr<PresentationBuilderChild> actor = - dont_AddRef(static_cast<PresentationBuilderChild*>(aActor)); - return true; -} - - -bool -PresentationChild::RecvNotifyAvailableChange( - nsTArray<nsString>&& aAvailabilityUrls, - const bool& aAvailable) -{ - if (mService) { - Unused << - NS_WARN_IF(NS_FAILED(mService->NotifyAvailableChange(aAvailabilityUrls, - aAvailable))); - } - return true; -} - -bool -PresentationChild::RecvNotifySessionStateChange(const nsString& aSessionId, - const uint16_t& aState, - const nsresult& aReason) -{ - if (mService) { - Unused << NS_WARN_IF(NS_FAILED(mService->NotifySessionStateChange(aSessionId, - aState, - aReason))); - } - return true; -} - -bool -PresentationChild::RecvNotifyMessage(const nsString& aSessionId, - const nsCString& aData, - const bool& aIsBinary) -{ - if (mService) { - Unused << NS_WARN_IF(NS_FAILED(mService->NotifyMessage(aSessionId, - aData, - aIsBinary))); - } - return true; -} - -bool -PresentationChild::RecvNotifySessionConnect(const uint64_t& aWindowId, - const nsString& aSessionId) -{ - if (mService) { - Unused << NS_WARN_IF(NS_FAILED(mService->NotifySessionConnect(aWindowId, aSessionId))); - } - return true; -} - -bool -PresentationChild::RecvNotifyCloseSessionTransport(const nsString& aSessionId, - const uint8_t& aRole, - const nsresult& aReason) -{ - if (mService) { - Unused << NS_WARN_IF(NS_FAILED( - mService->CloseContentSessionTransport(aSessionId, aRole, aReason))); - } - return true; -} - -/* - * Implementation of PresentationRequestChild - */ - -PresentationRequestChild::PresentationRequestChild(nsIPresentationServiceCallback* aCallback) - : mActorDestroyed(false) - , mCallback(aCallback) -{ - MOZ_COUNT_CTOR(PresentationRequestChild); -} - -PresentationRequestChild::~PresentationRequestChild() -{ - MOZ_COUNT_DTOR(PresentationRequestChild); - - mCallback = nullptr; -} - -void -PresentationRequestChild::ActorDestroy(ActorDestroyReason aWhy) -{ - mActorDestroyed = true; - mCallback = nullptr; -} - -bool -PresentationRequestChild::Recv__delete__(const nsresult& aResult) -{ - if (mActorDestroyed) { - return true; - } - - if (mCallback) { - if (NS_FAILED(aResult)) { - Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyError(aResult))); - } - } - - return true; -} - -bool -PresentationRequestChild::RecvNotifyRequestUrlSelected(const nsString& aUrl) -{ - Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifySuccess(aUrl))); - return true; -} diff --git a/dom/presentation/ipc/PresentationChild.h b/dom/presentation/ipc/PresentationChild.h deleted file mode 100644 index 1c625b8ce..000000000 --- a/dom/presentation/ipc/PresentationChild.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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_PresentationChild_h -#define mozilla_dom_PresentationChild_h - -#include "mozilla/dom/PPresentationBuilderChild.h" -#include "mozilla/dom/PPresentationChild.h" -#include "mozilla/dom/PPresentationRequestChild.h" - -class nsIPresentationServiceCallback; - -namespace mozilla { -namespace dom { - -class PresentationIPCService; - -class PresentationChild final : public PPresentationChild -{ -public: - explicit PresentationChild(PresentationIPCService* aService); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) override; - - virtual PPresentationRequestChild* - AllocPPresentationRequestChild(const PresentationIPCRequest& aRequest) override; - - virtual bool - DeallocPPresentationRequestChild(PPresentationRequestChild* aActor) override; - - bool RecvPPresentationBuilderConstructor(PPresentationBuilderChild* aActor, - const nsString& aSessionId, - const uint8_t& aRole) override; - - virtual PPresentationBuilderChild* - AllocPPresentationBuilderChild(const nsString& aSessionId, const uint8_t& aRole) override; - - virtual bool - DeallocPPresentationBuilderChild(PPresentationBuilderChild* aActor) override; - - virtual bool - RecvNotifyAvailableChange(nsTArray<nsString>&& aAvailabilityUrls, - const bool& aAvailable) override; - - virtual bool - RecvNotifySessionStateChange(const nsString& aSessionId, - const uint16_t& aState, - const nsresult& aReason) override; - - virtual bool - RecvNotifyMessage(const nsString& aSessionId, - const nsCString& aData, - const bool& aIsBinary) override; - - virtual bool - RecvNotifySessionConnect(const uint64_t& aWindowId, - const nsString& aSessionId) override; - - virtual bool - RecvNotifyCloseSessionTransport(const nsString& aSessionId, - const uint8_t& aRole, - const nsresult& aReason) override; - -private: - virtual ~PresentationChild(); - - bool mActorDestroyed = false; - RefPtr<PresentationIPCService> mService; -}; - -class PresentationRequestChild final : public PPresentationRequestChild -{ - friend class PresentationChild; - -public: - explicit PresentationRequestChild(nsIPresentationServiceCallback* aCallback); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) override; - - virtual bool - Recv__delete__(const nsresult& aResult) override; - - virtual bool - RecvNotifyRequestUrlSelected(const nsString& aUrl) override; - -private: - virtual ~PresentationRequestChild(); - - bool mActorDestroyed = false; - nsCOMPtr<nsIPresentationServiceCallback> mCallback; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationChild_h diff --git a/dom/presentation/ipc/PresentationContentSessionInfo.cpp b/dom/presentation/ipc/PresentationContentSessionInfo.cpp deleted file mode 100644 index 071ea924f..000000000 --- a/dom/presentation/ipc/PresentationContentSessionInfo.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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 "nsServiceManagerUtils.h" -#include "PresentationContentSessionInfo.h" -#include "PresentationIPCService.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(PresentationContentSessionInfo, - nsIPresentationSessionTransportCallback); - -nsresult -PresentationContentSessionInfo::Init() { - if (NS_WARN_IF(NS_FAILED(mTransport->SetCallback(this)))) { - return NS_ERROR_NOT_AVAILABLE; - } - if (NS_WARN_IF(NS_FAILED(mTransport->EnableDataNotification()))) { - return NS_ERROR_NOT_AVAILABLE; - } - return NS_OK; -} - -nsresult -PresentationContentSessionInfo::Send(const nsAString& aData) -{ - if (!mTransport) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->Send(aData); -} - -nsresult -PresentationContentSessionInfo::SendBinaryMsg(const nsACString& aData) -{ - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->SendBinaryMsg(aData); -} - -nsresult -PresentationContentSessionInfo::SendBlob(nsIDOMBlob* aBlob) -{ - if (NS_WARN_IF(!mTransport)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->SendBlob(aBlob); -} - -nsresult -PresentationContentSessionInfo::Close(nsresult aReason) -{ - if (!mTransport) { - return NS_ERROR_NOT_AVAILABLE; - } - - return mTransport->Close(aReason); -} - -// nsIPresentationSessionTransportCallback -NS_IMETHODIMP -PresentationContentSessionInfo::NotifyTransportReady() -{ - // do nothing since |onSessionTransport| implies this - return NS_OK; -} - -NS_IMETHODIMP -PresentationContentSessionInfo::NotifyTransportClosed(nsresult aReason) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // Nullify |mTransport| here so it won't try to re-close |mTransport| in - // potential subsequent |Shutdown| calls. - mTransport = nullptr; - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - return static_cast<PresentationIPCService*>(service.get())-> - NotifyTransportClosed(mSessionId, mRole, aReason); -} - -NS_IMETHODIMP -PresentationContentSessionInfo::NotifyData(const nsACString& aData, - bool aIsBinary) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr<nsIPresentationService> service = - do_GetService(PRESENTATION_SERVICE_CONTRACTID); - if (NS_WARN_IF(!service)) { - return NS_ERROR_NOT_AVAILABLE; - } - return static_cast<PresentationIPCService*>(service.get())-> - NotifyMessage(mSessionId, aData, aIsBinary); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationContentSessionInfo.h b/dom/presentation/ipc/PresentationContentSessionInfo.h deleted file mode 100644 index 447485dbd..000000000 --- a/dom/presentation/ipc/PresentationContentSessionInfo.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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_PresentationContentSessionInfo_h -#define mozilla_dom_PresentationContentSessionInfo_h - -#include "nsCOMPtr.h" -#include "nsIPresentationSessionTransport.h" - -namespace mozilla { -namespace dom { - -/** - * PresentationContentSessionInfo manages nsIPresentationSessionTransport and - * delegates the callbacks to PresentationIPCService. Only lives in content - * process. - */ -class PresentationContentSessionInfo final : public nsIPresentationSessionTransportCallback -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSESSIONTRANSPORTCALLBACK - - PresentationContentSessionInfo(const nsAString& aSessionId, - uint8_t aRole, - nsIPresentationSessionTransport* aTransport) - : mSessionId(aSessionId) - , mRole(aRole) - , mTransport(aTransport) - { - MOZ_ASSERT(XRE_IsContentProcess()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - MOZ_ASSERT(aTransport); - } - - nsresult Init(); - - nsresult Send(const nsAString& aData); - - nsresult SendBinaryMsg(const nsACString& aData); - - nsresult SendBlob(nsIDOMBlob* aBlob); - - nsresult Close(nsresult aReason); - -private: - virtual ~PresentationContentSessionInfo() {} - - nsString mSessionId; - uint8_t mRole; - nsCOMPtr<nsIPresentationSessionTransport> mTransport; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationContentSessionInfo_h diff --git a/dom/presentation/ipc/PresentationIPCService.cpp b/dom/presentation/ipc/PresentationIPCService.cpp deleted file mode 100644 index 8c85b239d..000000000 --- a/dom/presentation/ipc/PresentationIPCService.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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 "mozilla/dom/ContentChild.h" -#include "mozilla/dom/PermissionMessageUtils.h" -#include "mozilla/dom/PPresentation.h" -#include "mozilla/dom/TabParent.h" -#include "mozilla/ipc/InputStreamUtils.h" -#include "mozilla/ipc/URIUtils.h" -#include "nsGlobalWindow.h" -#include "nsIPresentationListener.h" -#include "PresentationCallbacks.h" -#include "PresentationChild.h" -#include "PresentationContentSessionInfo.h" -#include "PresentationIPCService.h" -#include "PresentationLog.h" - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::ipc; - -namespace { - -PresentationChild* sPresentationChild; - -} // anonymous - -NS_IMPL_ISUPPORTS(PresentationIPCService, - nsIPresentationService, - nsIPresentationAvailabilityListener) - -PresentationIPCService::PresentationIPCService() -{ - ContentChild* contentChild = ContentChild::GetSingleton(); - if (NS_WARN_IF(!contentChild)) { - return; - } - sPresentationChild = new PresentationChild(this); - Unused << - NS_WARN_IF(!contentChild->SendPPresentationConstructor(sPresentationChild)); -} - -/* virtual */ -PresentationIPCService::~PresentationIPCService() -{ - Shutdown(); - - mSessionListeners.Clear(); - mSessionInfoAtController.Clear(); - mSessionInfoAtReceiver.Clear(); - sPresentationChild = nullptr; -} - -NS_IMETHODIMP -PresentationIPCService::StartSession( - const nsTArray<nsString>& aUrls, - const nsAString& aSessionId, - const nsAString& aOrigin, - const nsAString& aDeviceId, - uint64_t aWindowId, - nsIDOMEventTarget* aEventTarget, - nsIPrincipal* aPrincipal, - nsIPresentationServiceCallback* aCallback, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) -{ - if (aWindowId != 0) { - AddRespondingSessionId(aWindowId, - aSessionId, - nsIPresentationService::ROLE_CONTROLLER); - } - - nsPIDOMWindowInner* window = - nsGlobalWindow::GetInnerWindowWithId(aWindowId)->AsInner(); - TabId tabId = TabParent::GetTabIdFrom(window->GetDocShell()); - - return SendRequest(aCallback, StartSessionRequest(aUrls, - nsString(aSessionId), - nsString(aOrigin), - nsString(aDeviceId), - aWindowId, - tabId, - IPC::Principal(aPrincipal))); -} - -NS_IMETHODIMP -PresentationIPCService::SendSessionMessage(const nsAString& aSessionId, - uint8_t aRole, - const nsAString& aData) -{ - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(!aData.IsEmpty()); - - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - // data channel session transport is maintained by content process - if (info) { - return info->Send(aData); - } - - return SendRequest(nullptr, SendSessionMessageRequest(nsString(aSessionId), - aRole, - nsString(aData))); -} - -NS_IMETHODIMP -PresentationIPCService::SendSessionBinaryMsg(const nsAString& aSessionId, - uint8_t aRole, - const nsACString &aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aData.IsEmpty()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - // data channel session transport is maintained by content process - if (info) { - return info->SendBinaryMsg(aData); - } - - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -PresentationIPCService::SendSessionBlob(const nsAString& aSessionId, - uint8_t aRole, - nsIDOMBlob* aBlob) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aSessionId.IsEmpty()); - MOZ_ASSERT(aRole == nsIPresentationService::ROLE_CONTROLLER || - aRole == nsIPresentationService::ROLE_RECEIVER); - MOZ_ASSERT(aBlob); - - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - // data channel session transport is maintained by content process - if (info) { - return info->SendBlob(aBlob); - } - - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -PresentationIPCService::CloseSession(const nsAString& aSessionId, - uint8_t aRole, - uint8_t aClosedReason) -{ - MOZ_ASSERT(!aSessionId.IsEmpty()); - - nsresult rv = SendRequest(nullptr, CloseSessionRequest(nsString(aSessionId), - aRole, - aClosedReason)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - if (info) { - return info->Close(NS_OK); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::TerminateSession(const nsAString& aSessionId, - uint8_t aRole) -{ - MOZ_ASSERT(!aSessionId.IsEmpty()); - - nsresult rv = SendRequest(nullptr, TerminateSessionRequest(nsString(aSessionId), aRole)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - if (info) { - return info->Close(NS_OK); - } - - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::ReconnectSession(const nsTArray<nsString>& aUrls, - const nsAString& aSessionId, - uint8_t aRole, - nsIPresentationServiceCallback* aCallback) -{ - MOZ_ASSERT(!aSessionId.IsEmpty()); - - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "Only controller can call ReconnectSession."); - return NS_ERROR_INVALID_ARG; - } - - return SendRequest(aCallback, ReconnectSessionRequest(aUrls, - nsString(aSessionId), - aRole)); -} - -NS_IMETHODIMP -PresentationIPCService::BuildTransport(const nsAString& aSessionId, - uint8_t aRole) -{ - MOZ_ASSERT(!aSessionId.IsEmpty()); - - if (aRole != nsIPresentationService::ROLE_CONTROLLER) { - MOZ_ASSERT(false, "Only controller can call ReconnectSession."); - return NS_ERROR_INVALID_ARG; - } - - return SendRequest(nullptr, BuildTransportRequest(nsString(aSessionId), - aRole)); -} - -nsresult -PresentationIPCService::SendRequest(nsIPresentationServiceCallback* aCallback, - const PresentationIPCRequest& aRequest) -{ - if (sPresentationChild) { - PresentationRequestChild* actor = new PresentationRequestChild(aCallback); - Unused << NS_WARN_IF(!sPresentationChild->SendPPresentationRequestConstructor(actor, aRequest)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::RegisterAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aAvailabilityUrls.IsEmpty()); - MOZ_ASSERT(aListener); - - nsTArray<nsString> addedUrls; - mAvailabilityManager.AddAvailabilityListener(aAvailabilityUrls, - aListener, - addedUrls); - - if (sPresentationChild && !addedUrls.IsEmpty()) { - Unused << - NS_WARN_IF( - !sPresentationChild->SendRegisterAvailabilityHandler(addedUrls)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::UnregisterAvailabilityListener( - const nsTArray<nsString>& aAvailabilityUrls, - nsIPresentationAvailabilityListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsTArray<nsString> removedUrls; - mAvailabilityManager.RemoveAvailabilityListener(aAvailabilityUrls, - aListener, - removedUrls); - - if (sPresentationChild && !removedUrls.IsEmpty()) { - Unused << - NS_WARN_IF( - !sPresentationChild->SendUnregisterAvailabilityHandler(removedUrls)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::RegisterSessionListener(const nsAString& aSessionId, - uint8_t aRole, - nsIPresentationSessionListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aListener); - - nsCOMPtr<nsIPresentationSessionListener> listener; - if (mSessionListeners.Get(aSessionId, getter_AddRefs(listener))) { - mSessionListeners.Put(aSessionId, aListener); - return NS_OK; - } - - mSessionListeners.Put(aSessionId, aListener); - if (sPresentationChild) { - Unused << - NS_WARN_IF(!sPresentationChild->SendRegisterSessionHandler( - nsString(aSessionId), aRole)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::UnregisterSessionListener(const nsAString& aSessionId, - uint8_t aRole) -{ - MOZ_ASSERT(NS_IsMainThread()); - - UntrackSessionInfo(aSessionId, aRole); - - mSessionListeners.Remove(aSessionId); - if (sPresentationChild) { - Unused << - NS_WARN_IF(!sPresentationChild->SendUnregisterSessionHandler( - nsString(aSessionId), aRole)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::RegisterRespondingListener(uint64_t aWindowId, - nsIPresentationRespondingListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mRespondingListeners.Put(aWindowId, aListener); - if (sPresentationChild) { - Unused << - NS_WARN_IF(!sPresentationChild->SendRegisterRespondingHandler(aWindowId)); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::UnregisterRespondingListener(uint64_t aWindowId) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mRespondingListeners.Remove(aWindowId); - if (sPresentationChild) { - Unused << - NS_WARN_IF(!sPresentationChild->SendUnregisterRespondingHandler( - aWindowId)); - } - return NS_OK; -} - -nsresult -PresentationIPCService::NotifySessionTransport(const nsString& aSessionId, - const uint8_t& aRole, - nsIPresentationSessionTransport* aTransport) -{ - RefPtr<PresentationContentSessionInfo> info = - new PresentationContentSessionInfo(aSessionId, aRole, aTransport); - - if (NS_WARN_IF(NS_FAILED(info->Init()))) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (aRole == nsIPresentationService::ROLE_CONTROLLER) { - mSessionInfoAtController.Put(aSessionId, info); - } else { - mSessionInfoAtReceiver.Put(aSessionId, info); - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::GetWindowIdBySessionId(const nsAString& aSessionId, - uint8_t aRole, - uint64_t* aWindowId) -{ - return GetWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); -} - -NS_IMETHODIMP -PresentationIPCService::UpdateWindowIdBySessionId(const nsAString& aSessionId, - uint8_t aRole, - const uint64_t aWindowId) -{ - return UpdateWindowIdBySessionIdInternal(aSessionId, aRole, aWindowId); -} - -nsresult -PresentationIPCService::NotifySessionStateChange(const nsAString& aSessionId, - uint16_t aState, - nsresult aReason) -{ - nsCOMPtr<nsIPresentationSessionListener> listener; - if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) { - return NS_OK; - } - - return listener->NotifyStateChange(aSessionId, aState, aReason); -} - -// Only used for OOP RTCDataChannel session transport case. -nsresult -PresentationIPCService::NotifyMessage(const nsAString& aSessionId, - const nsACString& aData, - const bool& aIsBinary) -{ - nsCOMPtr<nsIPresentationSessionListener> listener; - if (NS_WARN_IF(!mSessionListeners.Get(aSessionId, getter_AddRefs(listener)))) { - return NS_OK; - } - - return listener->NotifyMessage(aSessionId, aData, aIsBinary); -} - -// Only used for OOP RTCDataChannel session transport case. -nsresult -PresentationIPCService::NotifyTransportClosed(const nsAString& aSessionId, - uint8_t aRole, - nsresult aReason) -{ - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - Unused << NS_WARN_IF(!sPresentationChild->SendNotifyTransportClosed(nsString(aSessionId), aRole, aReason)); - return NS_OK; -} - -nsresult -PresentationIPCService::NotifySessionConnect(uint64_t aWindowId, - const nsAString& aSessionId) -{ - nsCOMPtr<nsIPresentationRespondingListener> listener; - if (NS_WARN_IF(!mRespondingListeners.Get(aWindowId, getter_AddRefs(listener)))) { - return NS_OK; - } - - return listener->NotifySessionConnect(aWindowId, aSessionId); -} - -NS_IMETHODIMP -PresentationIPCService::NotifyAvailableChange( - const nsTArray<nsString>& aAvailabilityUrls, - bool aAvailable) -{ - return mAvailabilityManager.DoNotifyAvailableChange(aAvailabilityUrls, - aAvailable); -} - -NS_IMETHODIMP -PresentationIPCService::NotifyReceiverReady( - const nsAString& aSessionId, - uint64_t aWindowId, - bool aIsLoading, - nsIPresentationTransportBuilderConstructor* aBuilderConstructor) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // No actual window uses 0 as its ID. - if (NS_WARN_IF(aWindowId == 0)) { - return NS_ERROR_NOT_AVAILABLE; - } - - // Track the responding info for an OOP receiver page. - AddRespondingSessionId(aWindowId, - aSessionId, - nsIPresentationService::ROLE_RECEIVER); - - Unused << NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsString(aSessionId), - aWindowId, - aIsLoading)); - - // Release mCallback after using aSessionId - // because aSessionId is held by mCallback. - mCallback = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -PresentationIPCService::UntrackSessionInfo(const nsAString& aSessionId, - uint8_t aRole) -{ - PRES_DEBUG("content %s:id[%s], role[%d]\n", __func__, - NS_ConvertUTF16toUTF8(aSessionId).get(), aRole); - - if (nsIPresentationService::ROLE_RECEIVER == aRole) { - // Terminate receiver page. - uint64_t windowId; - if (NS_SUCCEEDED(GetWindowIdBySessionIdInternal(aSessionId, - aRole, - &windowId))) { - NS_DispatchToMainThread(NS_NewRunnableFunction([windowId]() -> void { - PRES_DEBUG("Attempt to close window[%d]\n", windowId); - - if (auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId)) { - window->Close(); - } - })); - } - } - - // Remove the OOP responding info (if it has never been used). - RemoveRespondingSessionId(aSessionId, aRole); - - if (nsIPresentationService::ROLE_CONTROLLER == aRole) { - mSessionInfoAtController.Remove(aSessionId); - } else { - mSessionInfoAtReceiver.Remove(aSessionId); - } - - return NS_OK; -} - -void -PresentationIPCService::NotifyPresentationChildDestroyed() -{ - sPresentationChild = nullptr; -} - -nsresult -PresentationIPCService::MonitorResponderLoading(const nsAString& aSessionId, - nsIDocShell* aDocShell) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mCallback = new PresentationResponderLoadingCallback(aSessionId); - return mCallback->Init(aDocShell); -} - -nsresult -PresentationIPCService::CloseContentSessionTransport(const nsString& aSessionId, - uint8_t aRole, - nsresult aReason) -{ - RefPtr<PresentationContentSessionInfo> info = - GetSessionInfo(aSessionId, aRole); - if (NS_WARN_IF(!info)) { - return NS_ERROR_NOT_AVAILABLE; - } - - return info->Close(aReason); -} diff --git a/dom/presentation/ipc/PresentationIPCService.h b/dom/presentation/ipc/PresentationIPCService.h deleted file mode 100644 index 5eab7e68a..000000000 --- a/dom/presentation/ipc/PresentationIPCService.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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_PresentationIPCService_h -#define mozilla_dom_PresentationIPCService_h - -#include "mozilla/dom/PresentationServiceBase.h" -#include "nsIPresentationListener.h" -#include "nsIPresentationSessionTransport.h" -#include "nsIPresentationService.h" - -class nsIDocShell; - -namespace mozilla { -namespace dom { - -class PresentationIPCRequest; -class PresentationContentSessionInfo; -class PresentationResponderLoadingCallback; - -class PresentationIPCService final - : public nsIPresentationAvailabilityListener - , public nsIPresentationService - , public PresentationServiceBase<PresentationContentSessionInfo> -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER - NS_DECL_NSIPRESENTATIONSERVICE - - PresentationIPCService(); - - nsresult NotifySessionStateChange(const nsAString& aSessionId, - uint16_t aState, - nsresult aReason); - - nsresult NotifyMessage(const nsAString& aSessionId, - const nsACString& aData, - const bool& aIsBinary); - - nsresult NotifySessionConnect(uint64_t aWindowId, - const nsAString& aSessionId); - - void NotifyPresentationChildDestroyed(); - - nsresult MonitorResponderLoading(const nsAString& aSessionId, - nsIDocShell* aDocShell); - - nsresult NotifySessionTransport(const nsString& aSessionId, - const uint8_t& aRole, - nsIPresentationSessionTransport* transport); - - nsresult CloseContentSessionTransport(const nsString& aSessionId, - uint8_t aRole, - nsresult aReason); - -private: - virtual ~PresentationIPCService(); - nsresult SendRequest(nsIPresentationServiceCallback* aCallback, - const PresentationIPCRequest& aRequest); - - nsRefPtrHashtable<nsStringHashKey, - nsIPresentationSessionListener> mSessionListeners; - nsRefPtrHashtable<nsUint64HashKey, - nsIPresentationRespondingListener> mRespondingListeners; - RefPtr<PresentationResponderLoadingCallback> mCallback; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationIPCService_h diff --git a/dom/presentation/ipc/PresentationParent.cpp b/dom/presentation/ipc/PresentationParent.cpp deleted file mode 100644 index 02f60500a..000000000 --- a/dom/presentation/ipc/PresentationParent.cpp +++ /dev/null @@ -1,553 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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 "DCPresentationChannelDescription.h" -#include "mozilla/dom/ContentProcessManager.h" -#include "mozilla/ipc/InputStreamUtils.h" -#include "mozilla/Unused.h" -#include "nsIPresentationDeviceManager.h" -#include "nsIPresentationSessionTransport.h" -#include "nsIPresentationSessionTransportBuilder.h" -#include "nsServiceManagerUtils.h" -#include "PresentationBuilderParent.h" -#include "PresentationParent.h" -#include "PresentationService.h" -#include "PresentationSessionInfo.h" - -namespace mozilla { -namespace dom { - -namespace { - -class PresentationTransportBuilderConstructorIPC final : - public nsIPresentationTransportBuilderConstructor -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR - - explicit PresentationTransportBuilderConstructorIPC(PresentationParent* aParent) - : mParent(aParent) - { - } - -private: - virtual ~PresentationTransportBuilderConstructorIPC() = default; - - RefPtr<PresentationParent> mParent; -}; - -NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC, - nsIPresentationTransportBuilderConstructor) - -NS_IMETHODIMP -PresentationTransportBuilderConstructorIPC::CreateTransportBuilder( - uint8_t aType, - nsIPresentationSessionTransportBuilder** aRetval) -{ - if (NS_WARN_IF(!aRetval)) { - return NS_ERROR_INVALID_ARG; - } - - *aRetval = nullptr; - - if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP && - aType != nsIPresentationChannelDescription::TYPE_DATACHANNEL)) { - return NS_ERROR_INVALID_ARG; - } - - if (XRE_IsContentProcess()) { - MOZ_ASSERT(false, - "CreateTransportBuilder can only be invoked in parent process."); - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIPresentationSessionTransportBuilder> builder; - if (aType == nsIPresentationChannelDescription::TYPE_TCP) { - builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID); - } else { - builder = new PresentationBuilderParent(mParent); - } - - if (NS_WARN_IF(!builder)) { - return NS_ERROR_DOM_OPERATION_ERR; - } - - builder.forget(aRetval); - return NS_OK; -} - -} // anonymous namespace - -/* - * Implementation of PresentationParent - */ - -NS_IMPL_ISUPPORTS(PresentationParent, - nsIPresentationAvailabilityListener, - nsIPresentationSessionListener, - nsIPresentationRespondingListener) - -PresentationParent::PresentationParent() -{ - MOZ_COUNT_CTOR(PresentationParent); -} - -/* virtual */ PresentationParent::~PresentationParent() -{ - MOZ_COUNT_DTOR(PresentationParent); -} - -bool -PresentationParent::Init(ContentParentId aContentParentId) -{ - MOZ_ASSERT(!mService); - mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID); - mChildId = aContentParentId; - return NS_WARN_IF(!mService) ? false : true; -} - -void -PresentationParent::ActorDestroy(ActorDestroyReason aWhy) -{ - mActorDestroyed = true; - - for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) { - Unused << NS_WARN_IF(NS_FAILED(mService-> - UnregisterSessionListener(mSessionIdsAtController[i], - nsIPresentationService::ROLE_CONTROLLER))); - } - mSessionIdsAtController.Clear(); - - for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) { - Unused << NS_WARN_IF(NS_FAILED(mService-> - UnregisterSessionListener(mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER))); - } - mSessionIdsAtReceiver.Clear(); - - for (uint32_t i = 0; i < mWindowIds.Length(); i++) { - Unused << NS_WARN_IF(NS_FAILED(mService-> - UnregisterRespondingListener(mWindowIds[i]))); - } - mWindowIds.Clear(); - - if (!mContentAvailabilityUrls.IsEmpty()) { - mService->UnregisterAvailabilityListener(mContentAvailabilityUrls, this); - } - mService = nullptr; -} - -bool -PresentationParent::RecvPPresentationRequestConstructor( - PPresentationRequestParent* aActor, - const PresentationIPCRequest& aRequest) -{ - PresentationRequestParent* actor = static_cast<PresentationRequestParent*>(aActor); - - nsresult rv = NS_ERROR_FAILURE; - switch (aRequest.type()) { - case PresentationIPCRequest::TStartSessionRequest: - rv = actor->DoRequest(aRequest.get_StartSessionRequest()); - break; - case PresentationIPCRequest::TSendSessionMessageRequest: - rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest()); - break; - case PresentationIPCRequest::TCloseSessionRequest: - rv = actor->DoRequest(aRequest.get_CloseSessionRequest()); - break; - case PresentationIPCRequest::TTerminateSessionRequest: - rv = actor->DoRequest(aRequest.get_TerminateSessionRequest()); - break; - case PresentationIPCRequest::TReconnectSessionRequest: - rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest()); - break; - case PresentationIPCRequest::TBuildTransportRequest: - rv = actor->DoRequest(aRequest.get_BuildTransportRequest()); - break; - default: - MOZ_CRASH("Unknown PresentationIPCRequest type"); - } - - return NS_WARN_IF(NS_FAILED(rv)) ? false : true; -} - -PPresentationRequestParent* -PresentationParent::AllocPPresentationRequestParent( - const PresentationIPCRequest& aRequest) -{ - MOZ_ASSERT(mService); - RefPtr<PresentationRequestParent> actor = new PresentationRequestParent(mService, mChildId); - return actor.forget().take(); -} - -bool -PresentationParent::DeallocPPresentationRequestParent( - PPresentationRequestParent* aActor) -{ - RefPtr<PresentationRequestParent> actor = - dont_AddRef(static_cast<PresentationRequestParent*>(aActor)); - return true; -} - -PPresentationBuilderParent* -PresentationParent::AllocPPresentationBuilderParent(const nsString& aSessionId, - const uint8_t& aRole) -{ - NS_NOTREACHED("We should never be manually allocating AllocPPresentationBuilderParent actors"); - return nullptr; -} - -bool -PresentationParent::DeallocPPresentationBuilderParent( - PPresentationBuilderParent* aActor) -{ - return true; -} - -bool -PresentationParent::Recv__delete__() -{ - return true; -} - -bool -PresentationParent::RecvRegisterAvailabilityHandler( - nsTArray<nsString>&& aAvailabilityUrls) -{ - MOZ_ASSERT(mService); - - Unused << NS_WARN_IF(NS_FAILED(mService->RegisterAvailabilityListener( - aAvailabilityUrls, - this))); - mContentAvailabilityUrls.AppendElements(aAvailabilityUrls); - return true; -} - -bool -PresentationParent::RecvUnregisterAvailabilityHandler( - nsTArray<nsString>&& aAvailabilityUrls) -{ - MOZ_ASSERT(mService); - - Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterAvailabilityListener( - aAvailabilityUrls, - this))); - for (const auto& url : aAvailabilityUrls) { - mContentAvailabilityUrls.RemoveElement(url); - } - return true; -} - -/* virtual */ bool -PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId, - const uint8_t& aRole) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aSessionId, aRole, OtherPid()))) { - return true; - } - - if (nsIPresentationService::ROLE_CONTROLLER == aRole) { - mSessionIdsAtController.AppendElement(aSessionId); - } else { - mSessionIdsAtReceiver.AppendElement(aSessionId); - } - Unused << NS_WARN_IF(NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this))); - return true; -} - -/* virtual */ bool -PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId, - const uint8_t& aRole) -{ - MOZ_ASSERT(mService); - if (nsIPresentationService::ROLE_CONTROLLER == aRole) { - mSessionIdsAtController.RemoveElement(aSessionId); - } else { - mSessionIdsAtReceiver.RemoveElement(aSessionId); - } - Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole))); - return true; -} - -/* virtual */ bool -PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId) -{ - MOZ_ASSERT(mService); - - mWindowIds.AppendElement(aWindowId); - Unused << NS_WARN_IF(NS_FAILED(mService->RegisterRespondingListener(aWindowId, this))); - return true; -} - -/* virtual */ bool -PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId) -{ - MOZ_ASSERT(mService); - mWindowIds.RemoveElement(aWindowId); - Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterRespondingListener(aWindowId))); - return true; -} - -NS_IMETHODIMP -PresentationParent::NotifyAvailableChange(const nsTArray<nsString>& aAvailabilityUrls, - bool aAvailable) -{ - if (NS_WARN_IF(mActorDestroyed || - !SendNotifyAvailableChange(aAvailabilityUrls, - aAvailable))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationParent::NotifyStateChange(const nsAString& aSessionId, - uint16_t aState, - nsresult aReason) -{ - if (NS_WARN_IF(mActorDestroyed || - !SendNotifySessionStateChange(nsString(aSessionId), - aState, - aReason))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationParent::NotifyMessage(const nsAString& aSessionId, - const nsACString& aData, - bool aIsBinary) -{ - if (NS_WARN_IF(mActorDestroyed || - !SendNotifyMessage(nsString(aSessionId), - nsCString(aData), - aIsBinary))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -NS_IMETHODIMP -PresentationParent::NotifySessionConnect(uint64_t aWindowId, - const nsAString& aSessionId) -{ - if (NS_WARN_IF(mActorDestroyed || - !SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -bool -PresentationParent::RecvNotifyReceiverReady(const nsString& aSessionId, - const uint64_t& aWindowId, - const bool& aIsLoading) -{ - MOZ_ASSERT(mService); - - nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = - new PresentationTransportBuilderConstructorIPC(this); - Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(aSessionId, - aWindowId, - aIsLoading, - constructor))); - return true; -} - -bool -PresentationParent::RecvNotifyTransportClosed(const nsString& aSessionId, - const uint8_t& aRole, - const nsresult& aReason) -{ - MOZ_ASSERT(mService); - - Unused << NS_WARN_IF(NS_FAILED(mService->NotifyTransportClosed(aSessionId, aRole, aReason))); - return true; -} - -/* - * Implementation of PresentationRequestParent - */ - -NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback) - -PresentationRequestParent::PresentationRequestParent(nsIPresentationService* aService, - ContentParentId aContentParentId) - : mService(aService) - , mChildId(aContentParentId) -{ - MOZ_COUNT_CTOR(PresentationRequestParent); -} - -PresentationRequestParent::~PresentationRequestParent() -{ - MOZ_COUNT_DTOR(PresentationRequestParent); -} - -void -PresentationRequestParent::ActorDestroy(ActorDestroyReason aWhy) -{ - mActorDestroyed = true; - mService = nullptr; -} - -nsresult -PresentationRequestParent::DoRequest(const StartSessionRequest& aRequest) -{ - MOZ_ASSERT(mService); - - mSessionId = aRequest.sessionId(); - - nsCOMPtr<nsIDOMEventTarget> eventTarget; - ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); - RefPtr<TabParent> tp = - cpm->GetTopLevelTabParentByProcessAndTabId(mChildId, aRequest.tabId()); - if (tp) { - eventTarget = do_QueryInterface(tp->GetOwnerElement()); - } - - RefPtr<PresentationParent> parent = static_cast<PresentationParent*>(Manager()); - nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor = - new PresentationTransportBuilderConstructorIPC(parent); - return mService->StartSession(aRequest.urls(), aRequest.sessionId(), - aRequest.origin(), aRequest.deviceId(), - aRequest.windowId(), eventTarget, - aRequest.principal(), this, constructor); -} - -nsresult -PresentationRequestParent::DoRequest(const SendSessionMessageRequest& aRequest) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) { - return SendResponse(NS_ERROR_DOM_SECURITY_ERR); - } - - nsresult rv = mService->SendSessionMessage(aRequest.sessionId(), - aRequest.role(), - aRequest.data()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return SendResponse(rv); - } - return SendResponse(NS_OK); -} - -nsresult -PresentationRequestParent::DoRequest(const CloseSessionRequest& aRequest) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) { - return SendResponse(NS_ERROR_DOM_SECURITY_ERR); - } - - nsresult rv = mService->CloseSession(aRequest.sessionId(), - aRequest.role(), - aRequest.closedReason()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return SendResponse(rv); - } - return SendResponse(NS_OK); -} - -nsresult -PresentationRequestParent::DoRequest(const TerminateSessionRequest& aRequest) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) { - return SendResponse(NS_ERROR_DOM_SECURITY_ERR); - } - - nsresult rv = mService->TerminateSession(aRequest.sessionId(), aRequest.role()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return SendResponse(rv); - } - return SendResponse(NS_OK); -} - -nsresult -PresentationRequestParent::DoRequest(const ReconnectSessionRequest& aRequest) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) { - - // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec. - // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation - return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR); - } - - mSessionId = aRequest.sessionId(); - return mService->ReconnectSession(aRequest.urls(), - aRequest.sessionId(), - aRequest.role(), - this); -} - -nsresult -PresentationRequestParent::DoRequest(const BuildTransportRequest& aRequest) -{ - MOZ_ASSERT(mService); - - // Validate the accessibility (primarily for receiver side) so that a - // compromised child process can't fake the ID. - if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())-> - IsSessionAccessible(aRequest.sessionId(), aRequest.role(), OtherPid()))) { - return SendResponse(NS_ERROR_DOM_SECURITY_ERR); - } - - nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return SendResponse(rv); - } - return SendResponse(NS_OK); -} - -NS_IMETHODIMP -PresentationRequestParent::NotifySuccess(const nsAString& aUrl) -{ - Unused << SendNotifyRequestUrlSelected(nsString(aUrl)); - return SendResponse(NS_OK); -} - -NS_IMETHODIMP -PresentationRequestParent::NotifyError(nsresult aError) -{ - return SendResponse(aError); -} - -nsresult -PresentationRequestParent::SendResponse(nsresult aResult) -{ - if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/ipc/PresentationParent.h b/dom/presentation/ipc/PresentationParent.h deleted file mode 100644 index b038aa216..000000000 --- a/dom/presentation/ipc/PresentationParent.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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_PresentationParent_h__ -#define mozilla_dom_PresentationParent_h__ - -#include "mozilla/dom/ipc/IdType.h" -#include "mozilla/dom/PPresentationBuilderParent.h" -#include "mozilla/dom/PPresentationParent.h" -#include "mozilla/dom/PPresentationRequestParent.h" -#include "nsIPresentationListener.h" -#include "nsIPresentationService.h" -#include "nsIPresentationSessionTransportBuilder.h" - -namespace mozilla { -namespace dom { - -class PresentationParent final : public PPresentationParent - , public nsIPresentationAvailabilityListener - , public nsIPresentationSessionListener - , public nsIPresentationRespondingListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONAVAILABILITYLISTENER - NS_DECL_NSIPRESENTATIONSESSIONLISTENER - NS_DECL_NSIPRESENTATIONRESPONDINGLISTENER - - PresentationParent(); - - bool Init(ContentParentId aContentParentId); - - bool RegisterTransportBuilder(const nsString& aSessionId, const uint8_t& aRole); - - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - - virtual bool - RecvPPresentationRequestConstructor(PPresentationRequestParent* aActor, - const PresentationIPCRequest& aRequest) override; - - virtual PPresentationRequestParent* - AllocPPresentationRequestParent(const PresentationIPCRequest& aRequest) override; - - virtual bool - DeallocPPresentationRequestParent(PPresentationRequestParent* aActor) override; - - virtual PPresentationBuilderParent* - AllocPPresentationBuilderParent(const nsString& aSessionId, - const uint8_t& aRole) override; - - virtual bool - DeallocPPresentationBuilderParent( - PPresentationBuilderParent* aActor) override; - - virtual bool Recv__delete__() override; - - virtual bool RecvRegisterAvailabilityHandler( - nsTArray<nsString>&& aAvailabilityUrls) override; - - virtual bool RecvUnregisterAvailabilityHandler( - nsTArray<nsString>&& aAvailabilityUrls) override; - - virtual bool RecvRegisterSessionHandler(const nsString& aSessionId, - const uint8_t& aRole) override; - - virtual bool RecvUnregisterSessionHandler(const nsString& aSessionId, - const uint8_t& aRole) override; - - virtual bool RecvRegisterRespondingHandler(const uint64_t& aWindowId) override; - - virtual bool RecvUnregisterRespondingHandler(const uint64_t& aWindowId) override; - - virtual bool RecvNotifyReceiverReady(const nsString& aSessionId, - const uint64_t& aWindowId, - const bool& aIsLoading) override; - - virtual bool RecvNotifyTransportClosed(const nsString& aSessionId, - const uint8_t& aRole, - const nsresult& aReason) override; - -private: - virtual ~PresentationParent(); - - bool mActorDestroyed = false; - nsCOMPtr<nsIPresentationService> mService; - nsTArray<nsString> mSessionIdsAtController; - nsTArray<nsString> mSessionIdsAtReceiver; - nsTArray<uint64_t> mWindowIds; - ContentParentId mChildId; - nsTArray<nsString> mContentAvailabilityUrls; -}; - -class PresentationRequestParent final : public PPresentationRequestParent - , public nsIPresentationServiceCallback -{ - friend class PresentationParent; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONSERVICECALLBACK - - explicit PresentationRequestParent(nsIPresentationService* aService, - ContentParentId aContentParentId); - - virtual void ActorDestroy(ActorDestroyReason aWhy) override; - -private: - virtual ~PresentationRequestParent(); - - nsresult SendResponse(nsresult aResult); - - nsresult DoRequest(const StartSessionRequest& aRequest); - - nsresult DoRequest(const SendSessionMessageRequest& aRequest); - - nsresult DoRequest(const CloseSessionRequest& aRequest); - - nsresult DoRequest(const TerminateSessionRequest& aRequest); - - nsresult DoRequest(const ReconnectSessionRequest& aRequest); - - nsresult DoRequest(const BuildTransportRequest& aRequest); - - bool mActorDestroyed = false; - bool mNeedRegisterBuilder = false; - nsString mSessionId; - nsCOMPtr<nsIPresentationService> mService; - ContentParentId mChildId; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_PresentationParent_h__ diff --git a/dom/presentation/moz.build b/dom/presentation/moz.build deleted file mode 100644 index a7058382f..000000000 --- a/dom/presentation/moz.build +++ /dev/null @@ -1,89 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -DIRS += ['interfaces', 'provider'] - -XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] -MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] -MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini'] - -EXPORTS.mozilla.dom += [ - 'DCPresentationChannelDescription.h', - 'ipc/PresentationBuilderChild.h', - 'ipc/PresentationBuilderParent.h', - 'ipc/PresentationChild.h', - 'ipc/PresentationIPCService.h', - 'ipc/PresentationParent.h', - 'Presentation.h', - 'PresentationAvailability.h', - 'PresentationCallbacks.h', - 'PresentationConnection.h', - 'PresentationConnectionList.h', - 'PresentationDeviceManager.h', - 'PresentationReceiver.h', - 'PresentationRequest.h', - 'PresentationService.h', - 'PresentationServiceBase.h', - 'PresentationSessionInfo.h', - 'PresentationTCPSessionTransport.h', -] - -UNIFIED_SOURCES += [ - 'AvailabilityCollection.cpp', - 'ControllerConnectionCollection.cpp', - 'DCPresentationChannelDescription.cpp', - 'ipc/PresentationBuilderChild.cpp', - 'ipc/PresentationBuilderParent.cpp', - 'ipc/PresentationChild.cpp', - 'ipc/PresentationContentSessionInfo.cpp', - 'ipc/PresentationIPCService.cpp', - 'ipc/PresentationParent.cpp', - 'Presentation.cpp', - 'PresentationAvailability.cpp', - 'PresentationCallbacks.cpp', - 'PresentationConnection.cpp', - 'PresentationConnectionList.cpp', - 'PresentationDeviceManager.cpp', - 'PresentationReceiver.cpp', - 'PresentationRequest.cpp', - 'PresentationService.cpp', - 'PresentationSessionInfo.cpp', - 'PresentationSessionRequest.cpp', - 'PresentationTCPSessionTransport.cpp', - 'PresentationTerminateRequest.cpp', - 'PresentationTransportBuilderConstructor.cpp' -] - -EXTRA_COMPONENTS += [ - 'PresentationDataChannelSessionTransport.js', - 'PresentationDataChannelSessionTransport.manifest', - 'PresentationDeviceInfoManager.js', - 'PresentationDeviceInfoManager.manifest', -] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - EXTRA_COMPONENTS += [ - 'PresentationNetworkHelper.js', - 'PresentationNetworkHelper.manifest', - ] - -EXTRA_JS_MODULES += [ - 'PresentationDeviceInfoManager.jsm', -] - -IPDL_SOURCES += [ - 'ipc/PPresentation.ipdl', - 'ipc/PPresentationBuilder.ipdl', - 'ipc/PPresentationRequest.ipdl' -] - -LOCAL_INCLUDES += [ - '../base' -] - -include('/ipc/chromium/chromium-config.mozbuild') - -FINAL_LIBRARY = 'xul' diff --git a/dom/presentation/provider/AndroidCastDeviceProvider.js b/dom/presentation/provider/AndroidCastDeviceProvider.js deleted file mode 100644 index cf555f77b..000000000 --- a/dom/presentation/provider/AndroidCastDeviceProvider.js +++ /dev/null @@ -1,461 +0,0 @@ -/* -*- 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components, dump */ -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -// globals XPCOMUtils -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -// globals Services -Cu.import("resource://gre/modules/Services.jsm"); -// globals Messaging -Cu.import("resource://gre/modules/Messaging.jsm"); - -function log(str) { - // dump("-*- AndroidCastDeviceProvider -*-: " + str + "\n"); -} - -// Helper function: transfer nsIPresentationChannelDescription to json -function descriptionToString(aDescription) { - let json = {}; - json.type = aDescription.type; - switch(aDescription.type) { - case Ci.nsIPresentationChannelDescription.TYPE_TCP: - let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray); - json.tcpAddress = []; - for (let idx = 0; idx < addresses.length; idx++) { - let address = addresses.queryElementAt(idx, Ci.nsISupportsCString); - json.tcpAddress.push(address.data); - } - json.tcpPort = aDescription.tcpPort; - break; - case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL: - json.dataChannelSDP = aDescription.dataChannelSDP; - break; - } - return JSON.stringify(json); -} - -const TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE = "AndroidCastDevice:SyncDevice"; -const TOPIC_ANDROID_CAST_DEVICE_ADDED = "AndroidCastDevice:Added"; -const TOPIC_ANDROID_CAST_DEVICE_REMOVED = "AndroidCastDevice:Removed"; -const TOPIC_ANDROID_CAST_DEVICE_START = "AndroidCastDevice:Start"; -const TOPIC_ANDROID_CAST_DEVICE_STOP = "AndroidCastDevice:Stop"; -const TOPIC_PRESENTATION_VIEW_READY = "presentation-view-ready"; - -function LocalControlChannel(aProvider, aDeviceId, aRole) { - log("LocalControlChannel - create new LocalControlChannel for : " - + aRole); - this._provider = aProvider; - this._deviceId = aDeviceId; - this._role = aRole; -} - -LocalControlChannel.prototype = { - _listener: null, - _provider: null, - _deviceId: null, - _role: null, - _isOnTerminating: false, - _isOnDisconnecting: false, - _pendingConnected: false, - _pendingDisconnect: null, - _pendingOffer: null, - _pendingCandidate: null, - /* For the controller, it would be the control channel of the receiver. - * For the receiver, it would be the control channel of the controller. */ - _correspondingControlChannel: null, - - set correspondingControlChannel(aCorrespondingControlChannel) { - this._correspondingControlChannel = aCorrespondingControlChannel; - }, - - get correspondingControlChannel() { - return this._correspondingControlChannel; - }, - - notifyConnected: function LCC_notifyConnected() { - this._pendingDisconnect = null; - - if (!this._listener) { - this._pendingConnected = true; - } else { - this._listener.notifyConnected(); - } - }, - - onOffer: function LCC_onOffer(aOffer) { - if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) { - log("LocalControlChannel - onOffer of controller should not be called."); - return; - } - if (!this._listener) { - this._pendingOffer = aOffer; - } else { - this._listener.onOffer(aOffer); - } - }, - - onAnswer: function LCC_onAnswer(aAnswer) { - if (this._role == Ci.nsIPresentationService.ROLE_RECEIVER) { - log("LocalControlChannel - onAnswer of receiver should not be called."); - return; - } - this._listener.onAnswer(aAnswer); - }, - - notifyIceCandidate: function LCC_notifyIceCandidate(aCandidate) { - if (!this._listener) { - this._pendingCandidate = aCandidate; - } else { - this._listener.onIceCandidate(aCandidate); - } - }, - - // nsIPresentationControlChannel - get listener() { - return this._listener; - }, - - set listener(aListener) { - this._listener = aListener; - - if (!this._listener) { - return; - } - - if (this._pendingConnected) { - this.notifyConnected(); - this._pendingConnected = false; - } - - if (this._pendingOffer) { - this.onOffer(this._pendingOffer); - this._pendingOffer = null; - } - - if (this._pendingCandidate) { - this.notifyIceCandidate(this._pendingCandidate); - this._pendingCandidate = null; - } - - if (this._pendingDisconnect != null) { - this.disconnect(this._pendingDisconnect); - this._pendingDisconnect = null; - } - }, - - sendOffer: function LCC_sendOffer(aOffer) { - if (this._role == Ci.nsIPresentationService.ROLE_RECEIVER) { - log("LocalControlChannel - sendOffer of receiver should not be called."); - return; - } - log("LocalControlChannel - sendOffer aOffer=" + descriptionToString(aOffer)); - this._correspondingControlChannel.onOffer(aOffer); - }, - - sendAnswer: function LCC_sendAnswer(aAnswer) { - if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) { - log("LocalControlChannel - sendAnswer of controller should not be called."); - return; - } - log("LocalControlChannel - sendAnswer aAnswer=" + descriptionToString(aAnswer)); - this._correspondingControlChannel.onAnswer(aAnswer); - }, - - sendIceCandidate: function LCC_sendIceCandidate(aCandidate) { - log("LocalControlChannel - sendAnswer aCandidate=" + aCandidate); - this._correspondingControlChannel.notifyIceCandidate(aCandidate); - }, - - launch: function LCC_launch(aPresentationId, aUrl) { - log("LocalControlChannel - launch aPresentationId=" - + aPresentationId + " aUrl=" + aUrl); - // Create control channel for receiver directly. - let controlChannel = new LocalControlChannel(this._provider, - this._deviceId, - Ci.nsIPresentationService.ROLE_RECEIVER); - - // Set up the corresponding control channels for both controller and receiver. - this._correspondingControlChannel = controlChannel; - controlChannel._correspondingControlChannel = this; - - this._provider.onSessionRequest(this._deviceId, - aUrl, - aPresentationId, - controlChannel); - controlChannel.notifyConnected(); - }, - - terminate: function LCC_terminate(aPresentationId) { - log("LocalControlChannel - terminate aPresentationId=" - + aPresentationId); - - if (this._isOnTerminating) { - return; - } - - // Create control channel for corresponding role directly. - let correspondingRole = this._role == Ci.nsIPresentationService.ROLE_CONTROLLER - ? Ci.nsIPresentationService.ROLE_RECEIVER - : Ci.nsIPresentationService.ROLE_CONTROLLER; - let controlChannel = new LocalControlChannel(this._provider, - this._deviceId, - correspondingRole); - // Prevent the termination recursion. - controlChannel._isOnTerminating = true; - - // Set up the corresponding control channels for both controller and receiver. - this._correspondingControlChannel = controlChannel; - controlChannel._correspondingControlChannel = this; - - this._provider.onTerminateRequest(this._deviceId, - aPresentationId, - controlChannel, - this._role == Ci.nsIPresentationService.ROLE_RECEIVER); - controlChannel.notifyConnected(); - }, - - disconnect: function LCC_disconnect(aReason) { - log("LocalControlChannel - disconnect aReason=" + aReason); - - if (this._isOnDisconnecting) { - return; - } - - this._pendingOffer = null; - this._pendingCandidate = null; - this._pendingConnected = false; - - // this._pendingDisconnect is a nsresult. - // If it is null, it means no pending disconnect. - // If it is NS_OK, it means this control channel is disconnected normally. - // If it is other nsresult value, it means this control channel is - // disconnected abnormally. - - // Remote endpoint closes the control channel with abnormal reason. - if (aReason == Cr.NS_OK && - this._pendingDisconnect != null && - this._pendingDisconnect != Cr.NS_OK) { - aReason = this._pendingDisconnect; - } - - if (!this._listener) { - this._pendingDisconnect = aReason; - return; - } - - this._isOnDisconnecting = true; - this._correspondingControlChannel.disconnect(aReason); - this._listener.notifyDisconnected(aReason); - }, - - reconnect: function LCC_reconnect(aPresentationId, aUrl) { - log("1-UA on Android doesn't support reconnect."); - throw Cr.NS_ERROR_FAILURE; - }, - - classID: Components.ID("{c9be9450-e5c7-4294-a287-376971b017fd}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), -}; - -function ChromecastRemoteDisplayDevice(aProvider, aId, aName, aRole) { - this._provider = aProvider; - this._id = aId; - this._name = aName; - this._role = aRole; -} - -ChromecastRemoteDisplayDevice.prototype = { - _id: null, - _name: null, - _role: null, - _provider: null, - _ctrlChannel: null, - - update: function CRDD_update(aName) { - this._name = aName || this._name; - }, - - // nsIPresentationDevice - get id() { return this._id; }, - - get name() { return this._name; }, - - get type() { return "chromecast"; }, - - establishControlChannel: function CRDD_establishControlChannel() { - this._ctrlChannel = new LocalControlChannel(this._provider, - this._id, - this._role); - - if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) { - // Only connect to Chromecast for controller. - // Monitor the receiver being ready. - Services.obs.addObserver(this, TOPIC_PRESENTATION_VIEW_READY, true); - - // Launch Chromecast service in Android. - Messaging.sendRequestForResult({ - type: TOPIC_ANDROID_CAST_DEVICE_START, - id: this.id - }).then(result => { - log("Chromecast is connected."); - }).catch(error => { - log("Can not connect to Chromecast."); - // If Chromecast can not be launched, remove the observer. - Services.obs.removeObserver(this, TOPIC_PRESENTATION_VIEW_READY); - this._ctrlChannel.disconnect(Cr.NS_ERROR_FAILURE); - }); - } else { - // If establishControlChannel called from the receiver, we don't need to - // wait the 'presentation-view-ready' event. - this._ctrlChannel.notifyConnected(); - } - - return this._ctrlChannel; - }, - - disconnect: function CRDD_disconnect() { - // Disconnect from Chromecast. - Messaging.sendRequestForResult({ - type: TOPIC_ANDROID_CAST_DEVICE_STOP, - id: this.id - }); - }, - - isRequestedUrlSupported: function CRDD_isRequestedUrlSupported(aUrl) { - let url = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService) - .newURI(aUrl, null, null); - return url.scheme == "http" || url.scheme == "https"; - }, - - // nsIPresentationLocalDevice - get windowId() { return this._id; }, - - // nsIObserver - observe: function CRDD_observe(aSubject, aTopic, aData) { - if (aTopic == TOPIC_PRESENTATION_VIEW_READY) { - log("ChromecastRemoteDisplayDevice - observe: aTopic=" - + aTopic + " data=" + aData); - if (this.windowId === aData) { - Services.obs.removeObserver(this, TOPIC_PRESENTATION_VIEW_READY); - this._ctrlChannel.notifyConnected(); - } - } - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice, - Ci.nsIPresentationLocalDevice, - Ci.nsISupportsWeakReference, - Ci.nsIObserver]), -}; - -function AndroidCastDeviceProvider() { -} - -AndroidCastDeviceProvider.prototype = { - _listener: null, - _deviceList: new Map(), - - onSessionRequest: function APDP_onSessionRequest(aDeviceId, - aUrl, - aPresentationId, - aControlChannel) { - log("AndroidCastDeviceProvider - onSessionRequest" - + " aDeviceId=" + aDeviceId); - let device = this._deviceList.get(aDeviceId); - let receiverDevice = new ChromecastRemoteDisplayDevice(this, - device.id, - device.name, - Ci.nsIPresentationService.ROLE_RECEIVER); - this._listener.onSessionRequest(receiverDevice, - aUrl, - aPresentationId, - aControlChannel); - }, - - onTerminateRequest: function APDP_onTerminateRequest(aDeviceId, - aPresentationId, - aControlChannel, - aIsFromReceiver) { - log("AndroidCastDeviceProvider - onTerminateRequest" - + " aDeviceId=" + aDeviceId - + " aPresentationId=" + aPresentationId - + " aIsFromReceiver=" + aIsFromReceiver); - let device = this._deviceList.get(aDeviceId); - this._listener.onTerminateRequest(device, - aPresentationId, - aControlChannel, - aIsFromReceiver); - }, - - // nsIPresentationDeviceProvider - set listener(aListener) { - this._listener = aListener; - - // When unload this provider. - if (!this._listener) { - // remove observer - Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED); - Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED); - return; - } - - // Sync all device already found by Android. - Services.obs.notifyObservers(null, TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE, ""); - // Observer registration - Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED, false); - Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED, false); - }, - - get listener() { - return this._listener; - }, - - forceDiscovery: function APDP_forceDiscovery() { - // There is no API to do force discovery in Android SDK. - }, - - // nsIObserver - observe: function APDP_observe(aSubject, aTopic, aData) { - switch (aTopic) { - case TOPIC_ANDROID_CAST_DEVICE_ADDED: { - let deviceInfo = JSON.parse(aData); - let deviceId = deviceInfo.uuid; - - if (!this._deviceList.has(deviceId)) { - let device = new ChromecastRemoteDisplayDevice(this, - deviceInfo.uuid, - deviceInfo.friendlyName, - Ci.nsIPresentationService.ROLE_CONTROLLER); - this._deviceList.set(device.id, device); - this._listener.addDevice(device); - } else { - let device = this._deviceList.get(deviceId); - device.update(deviceInfo.friendlyName); - this._listener.updateDevice(device); - } - break; - } - case TOPIC_ANDROID_CAST_DEVICE_REMOVED: { - let deviceId = aData; - let device = this._deviceList.get(deviceId); - this._listener.removeDevice(device); - this._deviceList.delete(deviceId); - break; - } - } - }, - - classID: Components.ID("{7394f24c-dbc3-48c8-8a47-cd10169b7c6b}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsIPresentationDeviceProvider]), -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AndroidCastDeviceProvider]); diff --git a/dom/presentation/provider/AndroidCastDeviceProvider.manifest b/dom/presentation/provider/AndroidCastDeviceProvider.manifest deleted file mode 100644 index db2aa101b..000000000 --- a/dom/presentation/provider/AndroidCastDeviceProvider.manifest +++ /dev/null @@ -1,4 +0,0 @@ -# AndroidCastDeviceProvider.js -component {7394f24c-dbc3-48c8-8a47-cd10169b7c6b} AndroidCastDeviceProvider.js -contract @mozilla.org/presentation-device/android-cast-device-provider;1 {7394f24c-dbc3-48c8-8a47-cd10169b7c6b} -category presentation-device-provider AndroidCastDeviceProvider @mozilla.org/presentation-device/android-cast-device-provider;1 diff --git a/dom/presentation/provider/BuiltinProviders.manifest b/dom/presentation/provider/BuiltinProviders.manifest deleted file mode 100644 index 0ba7bcaa7..000000000 --- a/dom/presentation/provider/BuiltinProviders.manifest +++ /dev/null @@ -1,2 +0,0 @@ -component {f4079b8b-ede5-4b90-a112-5b415a931deb} PresentationControlService.js -contract @mozilla.org/presentation/control-service;1 {f4079b8b-ede5-4b90-a112-5b415a931deb} diff --git a/dom/presentation/provider/ControllerStateMachine.jsm b/dom/presentation/provider/ControllerStateMachine.jsm deleted file mode 100644 index b568a8e9a..000000000 --- a/dom/presentation/provider/ControllerStateMachine.jsm +++ /dev/null @@ -1,240 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components, dump */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["ControllerStateMachine"]; // jshint ignore:line - -const { utils: Cu } = Components; - -/* globals State, CommandType */ -Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm"); - -const DEBUG = false; -function debug(str) { - dump("-*- ControllerStateMachine: " + str + "\n"); -} - -var handlers = [ - function _initHandler(stateMachine, command) { - // shouldn't receive any command at init state. - DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line - }, - function _connectingHandler(stateMachine, command) { - switch (command.type) { - case CommandType.CONNECT_ACK: - stateMachine.state = State.CONNECTED; - stateMachine._notifyDeviceConnected(); - break; - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command. - break; - } - }, - function _connectedHandler(stateMachine, command) { - switch (command.type) { - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - case CommandType.LAUNCH_ACK: - stateMachine._notifyLaunch(command.presentationId); - break; - case CommandType.TERMINATE: - stateMachine._notifyTerminate(command.presentationId); - break; - case CommandType.TERMINATE_ACK: - stateMachine._notifyTerminate(command.presentationId); - break; - case CommandType.ANSWER: - case CommandType.ICE_CANDIDATE: - stateMachine._notifyChannelDescriptor(command); - break; - case CommandType.RECONNECT_ACK: - stateMachine._notifyReconnect(command.presentationId); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command. - break; - } - }, - function _closingHandler(stateMachine, command) { - switch (command.type) { - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command. - break; - } - }, - function _closedHandler(stateMachine, command) { - // ignore every command in closed state. - DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line - }, -]; - -function ControllerStateMachine(channel, deviceId) { - this.state = State.INIT; - this._channel = channel; - this._deviceId = deviceId; -} - -ControllerStateMachine.prototype = { - launch: function _launch(presentationId, url) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.LAUNCH, - presentationId: presentationId, - url: url, - }); - } - }, - - terminate: function _terminate(presentationId) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.TERMINATE, - presentationId: presentationId, - }); - } - }, - - terminateAck: function _terminateAck(presentationId) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.TERMINATE_ACK, - presentationId: presentationId, - }); - } - }, - - reconnect: function _reconnect(presentationId, url) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.RECONNECT, - presentationId: presentationId, - url: url, - }); - } - }, - - sendOffer: function _sendOffer(offer) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.OFFER, - offer: offer, - }); - } - }, - - sendAnswer: function _sendAnswer() { - // answer can only be sent by presenting UA. - debug("controller shouldn't generate answer"); - }, - - updateIceCandidate: function _updateIceCandidate(candidate) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.ICE_CANDIDATE, - candidate: candidate, - }); - } - }, - - onCommand: function _onCommand(command) { - handlers[this.state](this, command); - }, - - onChannelReady: function _onChannelReady() { - if (this.state === State.INIT) { - this._sendCommand({ - type: CommandType.CONNECT, - deviceId: this._deviceId - }); - this.state = State.CONNECTING; - } - }, - - onChannelClosed: function _onChannelClose(reason, isByRemote) { - switch (this.state) { - case State.CONNECTED: - if (isByRemote) { - this.state = State.CLOSED; - this._notifyDisconnected(reason); - } else { - this._sendCommand({ - type: CommandType.DISCONNECT, - reason: reason - }); - this.state = State.CLOSING; - this._closeReason = reason; - } - break; - case State.CLOSING: - if (isByRemote) { - this.state = State.CLOSED; - if (this._closeReason) { - reason = this._closeReason; - delete this._closeReason; - } - this._notifyDisconnected(reason); - } - break; - default: - DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line - break; - } - }, - - _sendCommand: function _sendCommand(command) { - this._channel.sendCommand(command); - }, - - _notifyDeviceConnected: function _notifyDeviceConnected() { - //XXX trigger following command - this._channel.notifyDeviceConnected(); - }, - - _notifyDisconnected: function _notifyDisconnected(reason) { - this._channel.notifyDisconnected(reason); - }, - - _notifyLaunch: function _notifyLaunch(presentationId) { - this._channel.notifyLaunch(presentationId); - }, - - _notifyTerminate: function _notifyTerminate(presentationId) { - this._channel.notifyTerminate(presentationId); - }, - - _notifyReconnect: function _notifyReconnect(presentationId) { - this._channel.notifyReconnect(presentationId); - }, - - _notifyChannelDescriptor: function _notifyChannelDescriptor(command) { - switch (command.type) { - case CommandType.ANSWER: - this._channel.notifyAnswer(command.answer); - break; - case CommandType.ICE_CANDIDATE: - this._channel.notifyIceCandidate(command.candidate); - break; - } - }, -}; - -this.ControllerStateMachine = ControllerStateMachine; // jshint ignore:line diff --git a/dom/presentation/provider/DeviceProviderHelpers.cpp b/dom/presentation/provider/DeviceProviderHelpers.cpp deleted file mode 100644 index 00b2c12f1..000000000 --- a/dom/presentation/provider/DeviceProviderHelpers.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=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 "DeviceProviderHelpers.h" - -#include "nsCOMPtr.h" -#include "nsIURI.h" -#include "nsNetUtil.h" - -namespace mozilla { -namespace dom { -namespace presentation { - -static const char* const kFxTVPresentationAppUrls[] = { - "app://fling-player.gaiamobile.org/index.html", - "app://notification-receiver.gaiamobile.org/index.html", - nullptr -}; - -/* static */ bool -DeviceProviderHelpers::IsCommonlySupportedScheme(const nsAString& aUrl) -{ - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl); - if (NS_FAILED(rv) || !uri) { - return false; - } - - nsAutoCString scheme; - uri->GetScheme(scheme); - if (scheme.LowerCaseEqualsLiteral("http") || - scheme.LowerCaseEqualsLiteral("https")) { - return true; - } - - return false; -} - -/* static */ bool -DeviceProviderHelpers::IsFxTVSupportedAppUrl(const nsAString& aUrl) -{ - // Check if matched with any presentation Apps on TV. - for (uint32_t i = 0; kFxTVPresentationAppUrls[i]; i++) { - if (aUrl.EqualsASCII(kFxTVPresentationAppUrls[i])) { - return true; - } - } - - return false; -} - -} // namespace presentation -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/provider/DeviceProviderHelpers.h b/dom/presentation/provider/DeviceProviderHelpers.h deleted file mode 100644 index 4bde09bed..000000000 --- a/dom/presentation/provider/DeviceProviderHelpers.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=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_presentation_DeviceProviderHelpers_h -#define mozilla_dom_presentation_DeviceProviderHelpers_h - -#include "nsString.h" - -namespace mozilla { -namespace dom { -namespace presentation { - -class DeviceProviderHelpers final -{ -public: - static bool IsCommonlySupportedScheme(const nsAString& aUrl); - static bool IsFxTVSupportedAppUrl(const nsAString& aUrl); - -private: - DeviceProviderHelpers() = delete; -}; - -} // namespace presentation -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_presentation_DeviceProviderHelpers_h diff --git a/dom/presentation/provider/DisplayDeviceProvider.cpp b/dom/presentation/provider/DisplayDeviceProvider.cpp deleted file mode 100644 index 3f88aba5e..000000000 --- a/dom/presentation/provider/DisplayDeviceProvider.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=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 "DisplayDeviceProvider.h" - -#include "DeviceProviderHelpers.h" -#include "mozilla/Logging.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "mozilla/Unused.h" -#include "nsIObserverService.h" -#include "nsIServiceManager.h" -#include "nsIWindowWatcher.h" -#include "nsNetUtil.h" -#include "nsPIDOMWindow.h" -#include "nsSimpleURI.h" -#include "nsTCPDeviceInfo.h" -#include "nsThreadUtils.h" - -static mozilla::LazyLogModule gDisplayDeviceProviderLog("DisplayDeviceProvider"); - -#define LOG(format) MOZ_LOG(gDisplayDeviceProviderLog, mozilla::LogLevel::Debug, format) - -#define DISPLAY_CHANGED_NOTIFICATION "display-changed" -#define DEFAULT_CHROME_FEATURES_PREF "toolkit.defaultChromeFeatures" -#define CHROME_REMOTE_URL_PREF "b2g.multiscreen.chrome_remote_url" -#define PREF_PRESENTATION_DISCOVERABLE_RETRY_MS "dom.presentation.discoverable.retry_ms" - -namespace mozilla { -namespace dom { -namespace presentation { - -/** - * This wrapper is used to break circular-reference problem. - */ -class DisplayDeviceProviderWrappedListener final - : public nsIPresentationControlServerListener -{ -public: - NS_DECL_ISUPPORTS - NS_FORWARD_SAFE_NSIPRESENTATIONCONTROLSERVERLISTENER(mListener) - - explicit DisplayDeviceProviderWrappedListener() = default; - - nsresult SetListener(DisplayDeviceProvider* aListener) - { - mListener = aListener; - return NS_OK; - } - -private: - virtual ~DisplayDeviceProviderWrappedListener() = default; - - DisplayDeviceProvider* mListener = nullptr; -}; - -NS_IMPL_ISUPPORTS(DisplayDeviceProviderWrappedListener, - nsIPresentationControlServerListener) - -NS_IMPL_ISUPPORTS(DisplayDeviceProvider::HDMIDisplayDevice, - nsIPresentationDevice, - nsIPresentationLocalDevice) - -// nsIPresentationDevice -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::GetId(nsACString& aId) -{ - aId = mWindowId; - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::GetName(nsACString& aName) -{ - aName = mName; - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::GetType(nsACString& aType) -{ - aType = mType; - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::GetWindowId(nsACString& aWindowId) -{ - aWindowId = mWindowId; - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice - ::EstablishControlChannel(nsIPresentationControlChannel** aControlChannel) -{ - nsresult rv = OpenTopLevelWindow(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<DisplayDeviceProvider> provider = mProvider.get(); - if (NS_WARN_IF(!provider)) { - return NS_ERROR_FAILURE; - } - return provider->Connect(this, aControlChannel); -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::Disconnect() -{ - nsresult rv = CloseTopLevelWindow(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK;; -} - -NS_IMETHODIMP -DisplayDeviceProvider::HDMIDisplayDevice::IsRequestedUrlSupported( - const nsAString& aRequestedUrl, - bool* aRetVal) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!aRetVal) { - return NS_ERROR_INVALID_POINTER; - } - - // 1-UA device only supports HTTP/HTTPS hosted receiver page. - *aRetVal = DeviceProviderHelpers::IsCommonlySupportedScheme(aRequestedUrl); - - return NS_OK; -} - -nsresult -DisplayDeviceProvider::HDMIDisplayDevice::OpenTopLevelWindow() -{ - MOZ_ASSERT(!mWindow); - - nsresult rv; - nsAutoCString flags(Preferences::GetCString(DEFAULT_CHROME_FEATURES_PREF)); - if (flags.IsEmpty()) { - return NS_ERROR_NOT_AVAILABLE; - } - flags.AppendLiteral(",mozDisplayId="); - flags.AppendInt(mScreenId); - - nsAutoCString remoteShellURLString(Preferences::GetCString(CHROME_REMOTE_URL_PREF)); - remoteShellURLString.AppendLiteral("#"); - remoteShellURLString.Append(mWindowId); - - // URI validation - nsCOMPtr<nsIURI> remoteShellURL; - rv = NS_NewURI(getter_AddRefs(remoteShellURL), remoteShellURLString); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = remoteShellURL->GetSpec(remoteShellURLString); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID); - MOZ_ASSERT(ww); - - rv = ww->OpenWindow(nullptr, - remoteShellURLString.get(), - "_blank", - flags.get(), - nullptr, - getter_AddRefs(mWindow)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -DisplayDeviceProvider::HDMIDisplayDevice::CloseTopLevelWindow() -{ - MOZ_ASSERT(mWindow); - - nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(mWindow); - nsresult rv = piWindow->Close(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -NS_IMPL_ISUPPORTS(DisplayDeviceProvider, - nsIObserver, - nsIPresentationDeviceProvider, - nsIPresentationControlServerListener) - -DisplayDeviceProvider::~DisplayDeviceProvider() -{ - Uninit(); -} - -nsresult -DisplayDeviceProvider::Init() -{ - // Provider must be initialized only once. - if (mInitialized) { - return NS_OK; - } - - nsresult rv; - - mServerRetryMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERABLE_RETRY_MS); - mServerRetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - MOZ_ASSERT(obs); - - obs->AddObserver(this, DISPLAY_CHANGED_NOTIFICATION, false); - - mDevice = new HDMIDisplayDevice(this); - - mWrappedListener = new DisplayDeviceProviderWrappedListener(); - rv = mWrappedListener->SetListener(this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mPresentationService = do_CreateInstance(PRESENTATION_CONTROL_SERVICE_CONTACT_ID, - &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = StartTCPService(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mInitialized = true; - return NS_OK; -} - -nsresult -DisplayDeviceProvider::Uninit() -{ - // Provider must be deleted only once. - if (!mInitialized) { - return NS_OK; - } - - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, DISPLAY_CHANGED_NOTIFICATION); - } - - // Remove device from device manager when the provider is uninit - RemoveExternalScreen(); - - AbortServerRetry(); - - mInitialized = false; - mWrappedListener->SetListener(nullptr); - return NS_OK; -} - -nsresult -DisplayDeviceProvider::StartTCPService() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsresult rv; - rv = mPresentationService->SetId(NS_LITERAL_CSTRING("DisplayDeviceProvider")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - uint16_t servicePort; - rv = mPresentationService->GetPort(&servicePort); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - /* - * If |servicePort| is non-zero, it means PresentationServer is running. - * Otherwise, we should make it start serving. - */ - if (servicePort) { - mPort = servicePort; - return NS_OK; - } - - rv = mPresentationService->SetListener(mWrappedListener); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - AbortServerRetry(); - - // 1-UA doesn't need encryption. - rv = mPresentationService->StartServer(/* aEncrypted = */ false, - /* aPort = */ 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -DisplayDeviceProvider::AbortServerRetry() -{ - if (mIsServerRetrying) { - mIsServerRetrying = false; - mServerRetryTimer->Cancel(); - } -} - -nsresult -DisplayDeviceProvider::AddExternalScreen() -{ - MOZ_ASSERT(mDeviceListener); - - nsresult rv; - nsCOMPtr<nsIPresentationDeviceListener> listener; - rv = GetListener(getter_AddRefs(listener)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = listener->AddDevice(mDevice); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -DisplayDeviceProvider::RemoveExternalScreen() -{ - MOZ_ASSERT(mDeviceListener); - - nsresult rv; - nsCOMPtr<nsIPresentationDeviceListener> listener; - rv = GetListener(getter_AddRefs(listener)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = listener->RemoveDevice(mDevice); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mDevice->Disconnect(); - return NS_OK; -} - -// nsIPresentationDeviceProvider -NS_IMETHODIMP -DisplayDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener) -{ - if (NS_WARN_IF(!aListener)) { - return NS_ERROR_INVALID_POINTER; - } - - nsresult rv; - nsCOMPtr<nsIPresentationDeviceListener> listener = - do_QueryReferent(mDeviceListener, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - listener.forget(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener) -{ - mDeviceListener = do_GetWeakReference(aListener); - nsresult rv = mDeviceListener ? Init() : Uninit(); - if(NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::ForceDiscovery() -{ - return NS_OK; -} - -// nsIPresentationControlServerListener -NS_IMETHODIMP -DisplayDeviceProvider::OnServerReady(uint16_t aPort, - const nsACString& aCertFingerprint) -{ - MOZ_ASSERT(NS_IsMainThread()); - mPort = aPort; - - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::OnServerStopped(nsresult aResult) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // Try restart server if it is stopped abnormally. - if (NS_FAILED(aResult)) { - mIsServerRetrying = true; - mServerRetryTimer->Init(this, mServerRetryMs, nsITimer::TYPE_ONE_SHOT); - } - - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDeviceInfo); - MOZ_ASSERT(aControlChannel); - - nsresult rv; - - nsCOMPtr<nsIPresentationDeviceListener> listener; - rv = GetListener(getter_AddRefs(listener)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!listener); - - rv = listener->OnSessionRequest(mDevice, - aUrl, - aPresentationId, - aControlChannel); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel, - bool aIsFromReceiver) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDeviceInfo); - MOZ_ASSERT(aControlChannel); - - nsresult rv; - - nsCOMPtr<nsIPresentationDeviceListener> listener; - rv = GetListener(getter_AddRefs(listener)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!listener); - - rv = listener->OnTerminateRequest(mDevice, - aPresentationId, - aControlChannel, - aIsFromReceiver); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -DisplayDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDeviceInfo); - MOZ_ASSERT(aControlChannel); - - nsresult rv; - - nsCOMPtr<nsIPresentationDeviceListener> listener; - rv = GetListener(getter_AddRefs(listener)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!listener); - - rv = listener->OnReconnectRequest(mDevice, - aUrl, - aPresentationId, - aControlChannel); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -// nsIObserver -NS_IMETHODIMP -DisplayDeviceProvider::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - if (!strcmp(aTopic, DISPLAY_CHANGED_NOTIFICATION)) { - nsCOMPtr<nsIDisplayInfo> displayInfo = do_QueryInterface(aSubject); - MOZ_ASSERT(displayInfo); - - int32_t type; - bool isConnected; - displayInfo->GetConnected(&isConnected); - // XXX The ID is as same as the type of display. - // See Bug 1138287 and nsScreenManagerGonk::AddScreen() for more detail. - displayInfo->GetId(&type); - - if (type == DisplayType::DISPLAY_EXTERNAL) { - nsresult rv = isConnected ? AddExternalScreen() : RemoveExternalScreen(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - } else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { - nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); - if (!timer) { - return NS_ERROR_UNEXPECTED; - } - - if (timer == mServerRetryTimer) { - mIsServerRetrying = false; - StartTCPService(); - } - } - - return NS_OK; -} - -nsresult -DisplayDeviceProvider::Connect(HDMIDisplayDevice* aDevice, - nsIPresentationControlChannel** aControlChannel) -{ - MOZ_ASSERT(aDevice); - MOZ_ASSERT(mPresentationService); - NS_ENSURE_ARG_POINTER(aControlChannel); - *aControlChannel = nullptr; - - nsCOMPtr<nsITCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(), - aDevice->Address(), - mPort, - EmptyCString()); - - return mPresentationService->Connect(deviceInfo, aControlChannel); -} - -} // namespace presentation -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/provider/DisplayDeviceProvider.h b/dom/presentation/provider/DisplayDeviceProvider.h deleted file mode 100644 index ebd5db394..000000000 --- a/dom/presentation/provider/DisplayDeviceProvider.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=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_presentation_provider_DisplayDeviceProvider_h -#define mozilla_dom_presentation_provider_DisplayDeviceProvider_h - -#include "mozilla/RefPtr.h" -#include "mozilla/WeakPtr.h" -#include "nsCOMPtr.h" -#include "nsIDOMWindow.h" -#include "nsIDisplayInfo.h" -#include "nsIObserver.h" -#include "nsIPresentationDeviceProvider.h" -#include "nsIPresentationLocalDevice.h" -#include "nsIPresentationControlService.h" -#include "nsITimer.h" -#include "nsIWindowWatcher.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsWeakReference.h" - -namespace mozilla { -namespace dom { -namespace presentation { - -// Consistent definition with the definition in -// widget/gonk/libdisplay/GonkDisplay.h. -enum DisplayType { - DISPLAY_PRIMARY, - DISPLAY_EXTERNAL, - DISPLAY_VIRTUAL, - NUM_DISPLAY_TYPES -}; - -class DisplayDeviceProviderWrappedListener; - -class DisplayDeviceProvider final : public nsIObserver - , public nsIPresentationDeviceProvider - , public nsIPresentationControlServerListener - , public SupportsWeakPtr<DisplayDeviceProvider> -{ -private: - class HDMIDisplayDevice final : public nsIPresentationLocalDevice - { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICE - NS_DECL_NSIPRESENTATIONLOCALDEVICE - - // mScreenId is as same as the definition of display type. - explicit HDMIDisplayDevice(DisplayDeviceProvider* aProvider) - : mScreenId(DisplayType::DISPLAY_EXTERNAL) - , mName("HDMI") - , mType("external") - , mWindowId("hdmi") - , mAddress("127.0.0.1") - , mProvider(aProvider) - {} - - nsresult OpenTopLevelWindow(); - nsresult CloseTopLevelWindow(); - - const nsCString& Id() const { return mWindowId; } - const nsCString& Address() const { return mAddress; } - - private: - virtual ~HDMIDisplayDevice() = default; - - // Due to the limitation of nsWinodw, mScreenId must be an integer. - // And mScreenId is also align to the display type defined in - // widget/gonk/libdisplay/GonkDisplay.h. - // HDMI display is DisplayType::DISPLAY_EXTERNAL. - uint32_t mScreenId; - nsCString mName; - nsCString mType; - nsCString mWindowId; - nsCString mAddress; - - nsCOMPtr<mozIDOMWindowProxy> mWindow; - // weak pointer - // Provider hold a strong pointer to the device. Use weak pointer to prevent - // the reference cycle. - WeakPtr<DisplayDeviceProvider> mProvider; - }; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - NS_DECL_NSIPRESENTATIONDEVICEPROVIDER - NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER - // For using WeakPtr when MOZ_REFCOUNTED_LEAK_CHECKING defined - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DisplayDeviceProvider) - - nsresult Connect(HDMIDisplayDevice* aDevice, - nsIPresentationControlChannel** aControlChannel); -private: - virtual ~DisplayDeviceProvider(); - - nsresult Init(); - nsresult Uninit(); - - nsresult AddExternalScreen(); - nsresult RemoveExternalScreen(); - - nsresult StartTCPService(); - - void AbortServerRetry(); - - // Now support HDMI display only and there should be only one HDMI display. - nsCOMPtr<nsIPresentationLocalDevice> mDevice = nullptr; - // weak pointer - // PresentationDeviceManager (mDeviceListener) hold strong pointer to - // DisplayDeviceProvider. Use nsWeakPtr to avoid reference cycle. - nsWeakPtr mDeviceListener = nullptr; - nsCOMPtr<nsIPresentationControlService> mPresentationService; - // Used to prevent reference cycle between DisplayDeviceProvider and - // TCPPresentationServer. - RefPtr<DisplayDeviceProviderWrappedListener> mWrappedListener; - - bool mInitialized = false; - uint16_t mPort; - - bool mIsServerRetrying = false; - uint32_t mServerRetryMs; - nsCOMPtr<nsITimer> mServerRetryTimer; -}; - -} // mozilla -} // dom -} // presentation - -#endif // mozilla_dom_presentation_provider_DisplayDeviceProvider_h - diff --git a/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp b/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp deleted file mode 100644 index 54849c9e3..000000000 --- a/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp +++ /dev/null @@ -1,774 +0,0 @@ -/* -*- 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 "LegacyMDNSDeviceProvider.h" - -#include "DeviceProviderHelpers.h" -#include "MainThreadUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "mozilla/Unused.h" -#include "nsComponentManagerUtils.h" -#include "nsIObserverService.h" -#include "nsIWritablePropertyBag2.h" -#include "nsServiceManagerUtils.h" -#include "nsTCPDeviceInfo.h" -#include "nsThreadUtils.h" -#include "nsIPropertyBag2.h" - -#define PREF_PRESENTATION_DISCOVERY_LEGACY "dom.presentation.discovery.legacy.enabled" -#define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms" -#define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name" - -#define LEGACY_SERVICE_TYPE "_mozilla_papi._tcp" - -#define LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID "@mozilla.org/presentation/legacy-control-service;1" - -static mozilla::LazyLogModule sLegacyMDNSProviderLogModule("LegacyMDNSDeviceProvider"); - -#undef LOG_I -#define LOG_I(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#undef LOG_E -#define LOG_E(...) MOZ_LOG(sLegacyMDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__)) - -namespace mozilla { -namespace dom { -namespace presentation { -namespace legacy { - -static const char* kObservedPrefs[] = { - PREF_PRESENTATION_DISCOVERY_LEGACY, - PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS, - PREF_PRESENTATION_DEVICE_NAME, - nullptr -}; - -namespace { - -static void -GetAndroidDeviceName(nsACString& aRetVal) -{ - nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); - MOZ_ASSERT(infoService, "Could not find a system info service"); - - Unused << NS_WARN_IF(NS_FAILED(infoService->GetPropertyAsACString( - NS_LITERAL_STRING("device"), aRetVal))); -} - -} //anonymous namespace - -/** - * This wrapper is used to break circular-reference problem. - */ -class DNSServiceWrappedListener final - : public nsIDNSServiceDiscoveryListener - , public nsIDNSServiceResolveListener -{ -public: - NS_DECL_ISUPPORTS - NS_FORWARD_SAFE_NSIDNSSERVICEDISCOVERYLISTENER(mListener) - NS_FORWARD_SAFE_NSIDNSSERVICERESOLVELISTENER(mListener) - - explicit DNSServiceWrappedListener() = default; - - nsresult SetListener(LegacyMDNSDeviceProvider* aListener) - { - mListener = aListener; - return NS_OK; - } - -private: - virtual ~DNSServiceWrappedListener() = default; - - LegacyMDNSDeviceProvider* mListener = nullptr; -}; - -NS_IMPL_ISUPPORTS(DNSServiceWrappedListener, - nsIDNSServiceDiscoveryListener, - nsIDNSServiceResolveListener) - -NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider, - nsIPresentationDeviceProvider, - nsIDNSServiceDiscoveryListener, - nsIDNSServiceResolveListener, - nsIObserver) - -LegacyMDNSDeviceProvider::~LegacyMDNSDeviceProvider() -{ - Uninit(); -} - -nsresult -LegacyMDNSDeviceProvider::Init() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mInitialized) { - return NS_OK; - } - - nsresult rv; - - mMulticastDNS = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mWrappedListener = new DNSServiceWrappedListener(); - if (NS_WARN_IF(NS_FAILED(rv = mWrappedListener->SetListener(this)))) { - return rv; - } - - mPresentationService = do_CreateInstance(LEGACY_PRESENTATION_CONTROL_SERVICE_CONTACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mDiscoveryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - Preferences::AddStrongObservers(this, kObservedPrefs); - - mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY); - mDiscoveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS); - mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME); - - // FIXME: Bug 1185806 - Provide a common device name setting. - if (mServiceName.IsEmpty()) { - GetAndroidDeviceName(mServiceName); - Unused << Preferences::SetCString(PREF_PRESENTATION_DEVICE_NAME, mServiceName); - } - - Unused << mPresentationService->SetId(mServiceName); - - if (mDiscoveryEnabled && NS_WARN_IF(NS_FAILED(rv = ForceDiscovery()))) { - return rv; - } - - mInitialized = true; - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::Uninit() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mInitialized) { - return NS_OK; - } - - ClearDevices(); - - Preferences::RemoveObservers(this, kObservedPrefs); - - StopDiscovery(NS_OK); - - mMulticastDNS = nullptr; - - if (mWrappedListener) { - mWrappedListener->SetListener(nullptr); - mWrappedListener = nullptr; - } - - mInitialized = false; - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::StopDiscovery(nsresult aReason) -{ - LOG_I("StopDiscovery (0x%08x)", aReason); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDiscoveryTimer); - - Unused << mDiscoveryTimer->Cancel(); - - if (mDiscoveryRequest) { - mDiscoveryRequest->Cancel(aReason); - mDiscoveryRequest = nullptr; - } - - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::Connect(Device* aDevice, - nsIPresentationControlChannel** aRetVal) -{ - MOZ_ASSERT(aDevice); - MOZ_ASSERT(mPresentationService); - - RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(), - aDevice->Address(), - aDevice->Port(), - EmptyCString()); - - return mPresentationService->Connect(deviceInfo, aRetVal); -} - -nsresult -LegacyMDNSDeviceProvider::AddDevice(const nsACString& aId, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - RefPtr<Device> device = new Device(aId, /* ID */ - aServiceName, - aServiceType, - aAddress, - aPort, - DeviceState::eActive, - this); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->AddDevice(device); - } - - mDevices.AppendElement(device); - - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::UpdateDevice(const uint32_t aIndex, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - if (NS_WARN_IF(aIndex >= mDevices.Length())) { - return NS_ERROR_INVALID_ARG; - } - - RefPtr<Device> device = mDevices[aIndex]; - device->Update(aServiceName, aServiceType, aAddress, aPort); - device->ChangeState(DeviceState::eActive); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->UpdateDevice(device); - } - - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::RemoveDevice(const uint32_t aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - if (NS_WARN_IF(aIndex >= mDevices.Length())) { - return NS_ERROR_INVALID_ARG; - } - - RefPtr<Device> device = mDevices[aIndex]; - - LOG_I("RemoveDevice: %s", device->Id().get()); - mDevices.RemoveElementAt(aIndex); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->RemoveDevice(device); - } - - return NS_OK; -} - -bool -LegacyMDNSDeviceProvider::FindDeviceById(const nsACString& aId, - uint32_t& aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - - RefPtr<Device> device = new Device(aId, - /* aName = */ EmptyCString(), - /* aType = */ EmptyCString(), - /* aHost = */ EmptyCString(), - /* aPort = */ 0, - /* aState = */ DeviceState::eUnknown, - /* aProvider = */ nullptr); - size_t index = mDevices.IndexOf(device, 0, DeviceIdComparator()); - - if (index == mDevices.NoIndex) { - return false; - } - - aIndex = index; - return true; -} - -bool -LegacyMDNSDeviceProvider::FindDeviceByAddress(const nsACString& aAddress, - uint32_t& aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - - RefPtr<Device> device = new Device(/* aId = */ EmptyCString(), - /* aName = */ EmptyCString(), - /* aType = */ EmptyCString(), - aAddress, - /* aPort = */ 0, - /* aState = */ DeviceState::eUnknown, - /* aProvider = */ nullptr); - size_t index = mDevices.IndexOf(device, 0, DeviceAddressComparator()); - - if (index == mDevices.NoIndex) { - return false; - } - - aIndex = index; - return true; -} - -void -LegacyMDNSDeviceProvider::MarkAllDevicesUnknown() -{ - MOZ_ASSERT(NS_IsMainThread()); - - for (auto& device : mDevices) { - device->ChangeState(DeviceState::eUnknown); - } -} - -void -LegacyMDNSDeviceProvider::ClearUnknownDevices() -{ - MOZ_ASSERT(NS_IsMainThread()); - - size_t i = mDevices.Length(); - while (i > 0) { - --i; - if (mDevices[i]->State() == DeviceState::eUnknown) { - Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i))); - } - } -} - -void -LegacyMDNSDeviceProvider::ClearDevices() -{ - MOZ_ASSERT(NS_IsMainThread()); - - size_t i = mDevices.Length(); - while (i > 0) { - --i; - Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i))); - } -} - -// nsIPresentationDeviceProvider -NS_IMETHODIMP -LegacyMDNSDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aListener)) { - return NS_ERROR_INVALID_POINTER; - } - - nsresult rv; - nsCOMPtr<nsIPresentationDeviceListener> listener = - do_QueryReferent(mDeviceListener, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - listener.forget(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mDeviceListener = do_GetWeakReference(aListener); - - nsresult rv; - if (mDeviceListener) { - if (NS_WARN_IF(NS_FAILED(rv = Init()))) { - return rv; - } - } else { - if (NS_WARN_IF(NS_FAILED(rv = Uninit()))) { - return rv; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::ForceDiscovery() -{ - LOG_I("ForceDiscovery (%d)", mDiscoveryEnabled); - MOZ_ASSERT(NS_IsMainThread()); - - if (!mDiscoveryEnabled) { - return NS_OK; - } - - MOZ_ASSERT(mDiscoveryTimer); - MOZ_ASSERT(mMulticastDNS); - - // if it's already discovering, extend existing discovery timeout. - nsresult rv; - if (mIsDiscovering) { - Unused << mDiscoveryTimer->Cancel(); - - if (NS_WARN_IF(NS_FAILED( rv = mDiscoveryTimer->Init(this, - mDiscoveryTimeoutMs, - nsITimer::TYPE_ONE_SHOT)))) { - return rv; - } - return NS_OK; - } - - StopDiscovery(NS_OK); - - if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->StartDiscovery( - NS_LITERAL_CSTRING(LEGACY_SERVICE_TYPE), - mWrappedListener, - getter_AddRefs(mDiscoveryRequest))))) { - return rv; - } - - return NS_OK; -} - -// nsIDNSServiceDiscoveryListener -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType) -{ - LOG_I("OnDiscoveryStarted"); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDiscoveryTimer); - - MarkAllDevicesUnknown(); - - nsresult rv; - if (NS_WARN_IF(NS_FAILED(rv = mDiscoveryTimer->Init(this, - mDiscoveryTimeoutMs, - nsITimer::TYPE_ONE_SHOT)))) { - return rv; - } - - mIsDiscovering = true; - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnDiscoveryStopped(const nsACString& aServiceType) -{ - LOG_I("OnDiscoveryStopped"); - MOZ_ASSERT(NS_IsMainThread()); - - ClearUnknownDevices(); - - mIsDiscovering = false; - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv ; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceFound: %s", serviceName.get()); - - if (mMulticastDNS) { - if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService( - aServiceInfo, mWrappedListener)))) { - return rv; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnServiceLost(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceLost: %s", serviceName.get()); - - nsAutoCString host; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) { - return rv; - } - - uint32_t index; - if (!FindDeviceById(host, index)) { - // given device was not found - return NS_OK; - } - - if (NS_WARN_IF(NS_FAILED(rv = RemoveDevice(index)))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnStartDiscoveryFailed(const nsACString& aServiceType, - int32_t aErrorCode) -{ - LOG_E("OnStartDiscoveryFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnStopDiscoveryFailed(const nsACString& aServiceType, - int32_t aErrorCode) -{ - LOG_E("OnStopDiscoveryFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -// nsIDNSServiceResolveListener -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceResolved: %s", serviceName.get()); - - nsAutoCString host; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) { - return rv; - } - - nsAutoCString address; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) { - return rv; - } - - uint16_t port; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) { - return rv; - } - - nsAutoCString serviceType; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceType(serviceType)))) { - return rv; - } - - uint32_t index; - if (FindDeviceById(host, index)) { - return UpdateDevice(index, - serviceName, - serviceType, - address, - port); - } else { - return AddDevice(host, - serviceName, - serviceType, - address, - port); - } - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo, - int32_t aErrorCode) -{ - LOG_E("OnResolveFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -// nsIObserver -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - - NS_ConvertUTF16toUTF8 data(aData); - LOG_I("Observe: topic = %s, data = %s", aTopic, data.get()); - - if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { - if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_LEGACY)) { - OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY_LEGACY)); - } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)) { - OnDiscoveryTimeoutChanged(Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)); - } else if (data.EqualsLiteral(PREF_PRESENTATION_DEVICE_NAME)) { - nsAdoptingCString newServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME); - if (!mServiceName.Equals(newServiceName)) { - OnServiceNameChanged(newServiceName); - } - } - } else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { - StopDiscovery(NS_OK); - } - - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::OnDiscoveryChanged(bool aEnabled) -{ - LOG_I("DiscoveryEnabled = %d\n", aEnabled); - MOZ_ASSERT(NS_IsMainThread()); - - mDiscoveryEnabled = aEnabled; - - if (mDiscoveryEnabled) { - return ForceDiscovery(); - } - - return StopDiscovery(NS_OK); -} - -nsresult -LegacyMDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs) -{ - LOG_I("OnDiscoveryTimeoutChanged = %d\n", aTimeoutMs); - MOZ_ASSERT(NS_IsMainThread()); - - mDiscoveryTimeoutMs = aTimeoutMs; - - return NS_OK; -} - -nsresult -LegacyMDNSDeviceProvider::OnServiceNameChanged(const nsACString& aServiceName) -{ - LOG_I("serviceName = %s\n", PromiseFlatCString(aServiceName).get()); - MOZ_ASSERT(NS_IsMainThread()); - - mServiceName = aServiceName; - mPresentationService->SetId(mServiceName); - - return NS_OK; -} - -// LegacyMDNSDeviceProvider::Device -NS_IMPL_ISUPPORTS(LegacyMDNSDeviceProvider::Device, - nsIPresentationDevice) - -// nsIPresentationDevice -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::GetId(nsACString& aId) -{ - aId = mId; - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::GetName(nsACString& aName) -{ - aName = mName; - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::GetType(nsACString& aType) -{ - aType = mType; - - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::EstablishControlChannel( - nsIPresentationControlChannel** aRetVal) -{ - if (!mProvider) { - return NS_ERROR_FAILURE; - } - - return mProvider->Connect(this, aRetVal); -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::Disconnect() -{ - // No need to do anything when disconnect. - return NS_OK; -} - -NS_IMETHODIMP -LegacyMDNSDeviceProvider::Device::IsRequestedUrlSupported( - const nsAString& aRequestedUrl, - bool* aRetVal) -{ - if (!aRetVal) { - return NS_ERROR_INVALID_POINTER; - } - - // Legacy TV 2.5 device only support a fixed set of presentation Apps. - *aRetVal = DeviceProviderHelpers::IsFxTVSupportedAppUrl(aRequestedUrl); - - return NS_OK; -} - -} // namespace legacy -} // namespace presentation -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/provider/LegacyMDNSDeviceProvider.h b/dom/presentation/provider/LegacyMDNSDeviceProvider.h deleted file mode 100644 index 33ba877d3..000000000 --- a/dom/presentation/provider/LegacyMDNSDeviceProvider.h +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- 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_presentation_provider_LegacyMDNSDeviceProvider_h -#define mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h - -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "nsICancelable.h" -#include "nsIDNSServiceDiscovery.h" -#include "nsIObserver.h" -#include "nsIPresentationDevice.h" -#include "nsIPresentationDeviceProvider.h" -#include "nsIPresentationControlService.h" -#include "nsITimer.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsWeakPtr.h" - -namespace mozilla { -namespace dom { -namespace presentation { -namespace legacy { - -class DNSServiceWrappedListener; -class MulticastDNSService; - -class LegacyMDNSDeviceProvider final - : public nsIPresentationDeviceProvider - , public nsIDNSServiceDiscoveryListener - , public nsIDNSServiceResolveListener - , public nsIObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICEPROVIDER - NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER - NS_DECL_NSIDNSSERVICERESOLVELISTENER - NS_DECL_NSIOBSERVER - - explicit LegacyMDNSDeviceProvider() = default; - nsresult Init(); - nsresult Uninit(); - -private: - enum class DeviceState : uint32_t { - eUnknown, - eActive - }; - - class Device final : public nsIPresentationDevice - { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICE - - explicit Device(const nsACString& aId, - const nsACString& aName, - const nsACString& aType, - const nsACString& aAddress, - const uint16_t aPort, - DeviceState aState, - LegacyMDNSDeviceProvider* aProvider) - : mId(aId) - , mName(aName) - , mType(aType) - , mAddress(aAddress) - , mPort(aPort) - , mState(aState) - , mProvider(aProvider) - { - } - - const nsCString& Id() const - { - return mId; - } - - const nsCString& Address() const - { - return mAddress; - } - - uint16_t Port() const - { - return mPort; - } - - DeviceState State() const - { - return mState; - } - - void ChangeState(DeviceState aState) - { - mState = aState; - } - - void Update(const nsACString& aName, - const nsACString& aType, - const nsACString& aAddress, - const uint16_t aPort) - { - mName = aName; - mType = aType; - mAddress = aAddress; - mPort = aPort; - } - - private: - virtual ~Device() = default; - - nsCString mId; - nsCString mName; - nsCString mType; - nsCString mAddress; - uint16_t mPort; - DeviceState mState; - LegacyMDNSDeviceProvider* mProvider; - }; - - struct DeviceIdComparator { - bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const { - return aA->Id() == aB->Id(); - } - }; - - struct DeviceAddressComparator { - bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const { - return aA->Address() == aB->Address(); - } - }; - - virtual ~LegacyMDNSDeviceProvider(); - nsresult StopDiscovery(nsresult aReason); - nsresult Connect(Device* aDevice, - nsIPresentationControlChannel** aRetVal); - - // device manipulation - nsresult AddDevice(const nsACString& aId, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort); - nsresult UpdateDevice(const uint32_t aIndex, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort); - nsresult RemoveDevice(const uint32_t aIndex); - bool FindDeviceById(const nsACString& aId, - uint32_t& aIndex); - - bool FindDeviceByAddress(const nsACString& aAddress, - uint32_t& aIndex); - - void MarkAllDevicesUnknown(); - void ClearUnknownDevices(); - void ClearDevices(); - - // preferences - nsresult OnDiscoveryChanged(bool aEnabled); - nsresult OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs); - nsresult OnServiceNameChanged(const nsACString& aServiceName); - - bool mInitialized = false; - nsWeakPtr mDeviceListener; - nsCOMPtr<nsIPresentationControlService> mPresentationService; - nsCOMPtr<nsIDNSServiceDiscovery> mMulticastDNS; - RefPtr<DNSServiceWrappedListener> mWrappedListener; - - nsCOMPtr<nsICancelable> mDiscoveryRequest; - - nsTArray<RefPtr<Device>> mDevices; - - bool mDiscoveryEnabled = false; - bool mIsDiscovering = false; - uint32_t mDiscoveryTimeoutMs; - nsCOMPtr<nsITimer> mDiscoveryTimer; - - nsCString mServiceName; -}; - -} // namespace legacy -} // namespace presentation -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_presentation_provider_LegacyMDNSDeviceProvider_h diff --git a/dom/presentation/provider/LegacyPresentationControlService.js b/dom/presentation/provider/LegacyPresentationControlService.js deleted file mode 100644 index b27177b63..000000000 --- a/dom/presentation/provider/LegacyPresentationControlService.js +++ /dev/null @@ -1,488 +0,0 @@ -/* 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components, dump */ -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -/* globals XPCOMUtils */ -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -/* globals Services */ -Cu.import("resource://gre/modules/Services.jsm"); -/* globals NetUtil */ -Cu.import("resource://gre/modules/NetUtil.jsm"); - -const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug"); -function log(aMsg) { - dump("-*- LegacyPresentationControlService.js: " + aMsg + "\n"); -} - -function LegacyPresentationControlService() { - DEBUG && log("LegacyPresentationControlService - ctor"); //jshint ignore:line - this._id = null; -} - -LegacyPresentationControlService.prototype = { - startServer: function() { - DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - get id() { - return this._id; - }, - - set id(aId) { - this._id = aId; - }, - - get port() { - return 0; - }, - - get version() { - return 0; - }, - - set listener(aListener) { //jshint ignore:line - DEBUG && log("LegacyPresentationControlService - doesn't support receiver mode"); //jshint ignore:line - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - get listener() { - return null; - }, - - connect: function(aDeviceInfo) { - if (!this.id) { - DEBUG && log("LegacyPresentationControlService - Id has not initialized; requestSession fails"); //jshint ignore:line - return null; - } - DEBUG && log("LegacyPresentationControlService - requestSession to " + aDeviceInfo.id); //jshint ignore:line - - let sts = Cc["@mozilla.org/network/socket-transport-service;1"] - .getService(Ci.nsISocketTransportService); - - let socketTransport; - try { - socketTransport = sts.createTransport(null, - 0, - aDeviceInfo.address, - aDeviceInfo.port, - null); - } catch (e) { - DEBUG && log("LegacyPresentationControlService - createTransport throws: " + e); //jshint ignore:line - // Pop the exception to |TCPDevice.establishControlChannel| - throw Cr.NS_ERROR_FAILURE; - } - return new LegacyTCPControlChannel(this.id, - socketTransport, - aDeviceInfo); - }, - - close: function() { - DEBUG && log("LegacyPresentationControlService - close"); //jshint ignore:line - }, - - classID: Components.ID("{b21816fe-8aff-4811-86d2-85a7444c557e}"), - QueryInterface : XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), -}; - -function ChannelDescription(aInit) { - this._type = aInit.type; - switch (this._type) { - case Ci.nsIPresentationChannelDescription.TYPE_TCP: - this._tcpAddresses = Cc["@mozilla.org/array;1"] - .createInstance(Ci.nsIMutableArray); - for (let address of aInit.tcpAddress) { - let wrapper = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); - wrapper.data = address; - this._tcpAddresses.appendElement(wrapper, false); - } - - this._tcpPort = aInit.tcpPort; - break; - case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL: - this._dataChannelSDP = aInit.dataChannelSDP; - break; - } -} - -ChannelDescription.prototype = { - _type: 0, - _tcpAddresses: null, - _tcpPort: 0, - _dataChannelSDP: "", - - get type() { - return this._type; - }, - - get tcpAddress() { - return this._tcpAddresses; - }, - - get tcpPort() { - return this._tcpPort; - }, - - get dataChannelSDP() { - return this._dataChannelSDP; - }, - - classID: Components.ID("{d69fc81c-4f40-47a3-97e6-b4cf5db2294e}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), -}; - -// Helper function: transfer nsIPresentationChannelDescription to json -function discriptionAsJson(aDescription) { - let json = {}; - json.type = aDescription.type; - switch(aDescription.type) { - case Ci.nsIPresentationChannelDescription.TYPE_TCP: - let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray); - json.tcpAddress = []; - for (let idx = 0; idx < addresses.length; idx++) { - let address = addresses.queryElementAt(idx, Ci.nsISupportsCString); - json.tcpAddress.push(address.data); - } - json.tcpPort = aDescription.tcpPort; - break; - case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL: - json.dataChannelSDP = aDescription.dataChannelSDP; - break; - } - return json; -} - -function LegacyTCPControlChannel(id, - transport, - deviceInfo) { - DEBUG && log("create LegacyTCPControlChannel"); //jshint ignore:line - this._deviceInfo = deviceInfo; - this._transport = transport; - - this._id = id; - - let currentThread = Services.tm.currentThread; - transport.setEventSink(this, currentThread); - - this._input = this._transport.openInputStream(0, 0, 0) - .QueryInterface(Ci.nsIAsyncInputStream); - this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener), - Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY, - 0, - currentThread); - - this._output = this._transport - .openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0); -} - -LegacyTCPControlChannel.prototype = { - _connected: false, - _pendingOpen: false, - _pendingAnswer: null, - _pendingClose: null, - _pendingCloseReason: null, - - _sendMessage: function(aJSONData, aOnThrow) { - if (!aOnThrow) { - aOnThrow = function(e) {throw e.result;}; - } - - if (!aJSONData) { - aOnThrow(); - return; - } - - if (!this._connected) { - DEBUG && log("LegacyTCPControlChannel - send" + aJSONData.type + " fails"); //jshint ignore:line - throw Cr.NS_ERROR_FAILURE; - } - - try { - this._send(aJSONData); - } catch (e) { - aOnThrow(e); - } - }, - - _sendInit: function() { - let msg = { - type: "requestSession:Init", - presentationId: this._presentationId, - url: this._url, - id: this._id, - }; - - this._sendMessage(msg, function(e) { - this.disconnect(); - this._notifyDisconnected(e.result); - }); - }, - - launch: function(aPresentationId, aUrl) { - this._presentationId = aPresentationId; - this._url = aUrl; - - this._sendInit(); - }, - - terminate: function() { - // Legacy protocol doesn't support extra terminate protocol. - // Trigger error handling for browser to shutdown all the resource locally. - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - sendOffer: function(aOffer) { - let msg = { - type: "requestSession:Offer", - presentationId: this._presentationId, - offer: discriptionAsJson(aOffer), - }; - this._sendMessage(msg); - }, - - sendAnswer: function(aAnswer) { //jshint ignore:line - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - sendIceCandidate: function(aCandidate) { - let msg = { - type: "requestSession:IceCandidate", - presentationId: this._presentationId, - iceCandidate: aCandidate, - }; - this._sendMessage(msg); - }, - // may throw an exception - _send: function(aMsg) { - DEBUG && log("LegacyTCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); //jshint ignore:line - - /** - * XXX In TCP streaming, it is possible that more than one message in one - * TCP packet. We use line delimited JSON to identify where one JSON encoded - * object ends and the next begins. Therefore, we do not allow newline - * characters whithin the whole message, and add a newline at the end. - * Please see the parser code in |onDataAvailable|. - */ - let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n"; - try { - this._output.write(message, message.length); - } catch(e) { - DEBUG && log("LegacyTCPControlChannel - Failed to send message: " + e.name); //jshint ignore:line - throw e; - } - }, - - // nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait) - // Only used for detecting connection refused - onInputStreamReady: function(aStream) { - try { - aStream.available(); - } catch (e) { - DEBUG && log("LegacyTCPControlChannel - onInputStreamReady error: " + e.name); //jshint ignore:line - // NS_ERROR_CONNECTION_REFUSED - this._listener.notifyDisconnected(e.result); - } - }, - - // nsITransportEventSink (Triggered by nsISocketTransport.setEventSink) - onTransportStatus: function(aTransport, aStatus, aProg, aProgMax) { //jshint ignore:line - DEBUG && log("LegacyTCPControlChannel - onTransportStatus: " - + aStatus.toString(16)); //jshint ignore:line - if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) { - this._connected = true; - - if (!this._pump) { - this._createInputStreamPump(); - } - - this._notifyConnected(); - } - }, - - // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead) - onStartRequest: function() { - DEBUG && log("LegacyTCPControlChannel - onStartRequest"); //jshint ignore:line - }, - - // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead) - onStopRequest: function(aRequest, aContext, aStatus) { - DEBUG && log("LegacyTCPControlChannel - onStopRequest: " + aStatus); //jshint ignore:line - this.disconnect(aStatus); - this._notifyDisconnected(aStatus); - }, - - // nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead) - onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) { //jshint ignore:line - let data = NetUtil.readInputStreamToString(aInputStream, - aInputStream.available()); - DEBUG && log("LegacyTCPControlChannel - onDataAvailable: " + data); //jshint ignore:line - - // Parser of line delimited JSON. Please see |_send| for more informaiton. - let jsonArray = data.split("\n"); - jsonArray.pop(); - for (let json of jsonArray) { - let msg; - try { - msg = JSON.parse(json); - } catch (e) { - DEBUG && log("LegacyTCPSignalingChannel - error in parsing json: " + e); //jshint ignore:line - } - - this._handleMessage(msg); - } - }, - - _createInputStreamPump: function() { - DEBUG && log("LegacyTCPControlChannel - create pump"); //jshint ignore:line - this._pump = Cc["@mozilla.org/network/input-stream-pump;1"]. - createInstance(Ci.nsIInputStreamPump); - this._pump.init(this._input, -1, -1, 0, 0, false); - this._pump.asyncRead(this, null); - }, - - // Handle command from remote side - _handleMessage: function(aMsg) { - DEBUG && log("LegacyTCPControlChannel - handleMessage from " - + JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); //jshint ignore:line - switch (aMsg.type) { - case "requestSession:Answer": { - this._onAnswer(aMsg.answer); - break; - } - case "requestSession:IceCandidate": { - this._listener.onIceCandidate(aMsg.iceCandidate); - break; - } - case "requestSession:CloseReason": { - this._pendingCloseReason = aMsg.reason; - break; - } - } - }, - - get listener() { - return this._listener; - }, - - set listener(aListener) { - DEBUG && log("LegacyTCPControlChannel - set listener: " + aListener); //jshint ignore:line - if (!aListener) { - this._listener = null; - return; - } - - this._listener = aListener; - if (this._pendingOpen) { - this._pendingOpen = false; - DEBUG && log("LegacyTCPControlChannel - notify pending opened"); //jshint ignore:line - this._listener.notifyConnected(); - } - - if (this._pendingAnswer) { - let answer = this._pendingAnswer; - DEBUG && log("LegacyTCPControlChannel - notify pending answer: " + - JSON.stringify(answer)); // jshint ignore:line - this._listener.onAnswer(new ChannelDescription(answer)); - this._pendingAnswer = null; - } - - if (this._pendingClose) { - DEBUG && log("LegacyTCPControlChannel - notify pending closed"); //jshint ignore:line - this._notifyDisconnected(this._pendingCloseReason); - this._pendingClose = null; - } - }, - - /** - * These functions are designed to handle the interaction with listener - * appropriately. |_FUNC| is to handle |this._listener.FUNC|. - */ - _onAnswer: function(aAnswer) { - if (!this._connected) { - return; - } - if (!this._listener) { - this._pendingAnswer = aAnswer; - return; - } - DEBUG && log("LegacyTCPControlChannel - notify answer: " + JSON.stringify(aAnswer)); //jshint ignore:line - this._listener.onAnswer(new ChannelDescription(aAnswer)); - }, - - _notifyConnected: function() { - this._connected = true; - this._pendingClose = false; - this._pendingCloseReason = Cr.NS_OK; - - if (!this._listener) { - this._pendingOpen = true; - return; - } - - DEBUG && log("LegacyTCPControlChannel - notify opened"); //jshint ignore:line - this._listener.notifyConnected(); - }, - - _notifyDisconnected: function(aReason) { - this._connected = false; - this._pendingOpen = false; - this._pendingAnswer = null; - - // Remote endpoint closes the control channel with abnormal reason. - if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) { - aReason = this._pendingCloseReason; - } - - if (!this._listener) { - this._pendingClose = true; - this._pendingCloseReason = aReason; - return; - } - - DEBUG && log("LegacyTCPControlChannel - notify closed"); //jshint ignore:line - this._listener.notifyDisconnected(aReason); - }, - - disconnect: function(aReason) { - DEBUG && log("LegacyTCPControlChannel - close with reason: " + aReason); //jshint ignore:line - - if (this._connected) { - // default reason is NS_OK - if (typeof aReason !== "undefined" && aReason !== Cr.NS_OK) { - let msg = { - type: "requestSession:CloseReason", - presentationId: this._presentationId, - reason: aReason, - }; - this._sendMessage(msg); - this._pendingCloseReason = aReason; - } - - this._transport.setEventSink(null, null); - this._pump = null; - - this._input.close(); - this._output.close(); - - this._connected = false; - } - }, - - reconnect: function() { - // Legacy protocol doesn't support extra reconnect protocol. - // Trigger error handling for browser to shutdown all the resource locally. - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - classID: Components.ID("{4027ce3d-06e3-4d06-a235-df329cb0d411}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel, - Ci.nsIStreamListener]), -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LegacyPresentationControlService]); //jshint ignore:line diff --git a/dom/presentation/provider/LegacyProviders.manifest b/dom/presentation/provider/LegacyProviders.manifest deleted file mode 100644 index 9408da063..000000000 --- a/dom/presentation/provider/LegacyProviders.manifest +++ /dev/null @@ -1,2 +0,0 @@ -component {b21816fe-8aff-4811-86d2-85a7444c557e} LegacyPresentationControlService.js -contract @mozilla.org/presentation/legacy-control-service;1 {b21816fe-8aff-4811-86d2-85a7444c557e} diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp deleted file mode 100644 index 0cab915ac..000000000 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ /dev/null @@ -1,1249 +0,0 @@ -/* -*- 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 "MulticastDNSDeviceProvider.h" - -#include "DeviceProviderHelpers.h" -#include "MainThreadUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "mozilla/Unused.h" -#include "nsComponentManagerUtils.h" -#include "nsIObserverService.h" -#include "nsIWritablePropertyBag2.h" -#include "nsServiceManagerUtils.h" -#include "nsTCPDeviceInfo.h" -#include "nsThreadUtils.h" - -#ifdef MOZ_WIDGET_ANDROID -#include "nsIPropertyBag2.h" -#endif // MOZ_WIDGET_ANDROID - -#define PREF_PRESENTATION_DISCOVERY "dom.presentation.discovery.enabled" -#define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms" -#define PREF_PRESENTATION_DISCOVERABLE "dom.presentation.discoverable" -#define PREF_PRESENTATION_DISCOVERABLE_ENCRYPTED "dom.presentation.discoverable.encrypted" -#define PREF_PRESENTATION_DISCOVERABLE_RETRY_MS "dom.presentation.discoverable.retry_ms" -#define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name" - -#define SERVICE_TYPE "_presentation-ctrl._tcp" -#define PROTOCOL_VERSION_TAG "version" -#define CERT_FINGERPRINT_TAG "certFingerprint" - -static mozilla::LazyLogModule sMulticastDNSProviderLogModule("MulticastDNSDeviceProvider"); - -#undef LOG_I -#define LOG_I(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#undef LOG_E -#define LOG_E(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__)) - -namespace mozilla { -namespace dom { -namespace presentation { - -static const char* kObservedPrefs[] = { - PREF_PRESENTATION_DISCOVERY, - PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS, - PREF_PRESENTATION_DISCOVERABLE, - PREF_PRESENTATION_DEVICE_NAME, - nullptr -}; - -namespace { - -#ifdef MOZ_WIDGET_ANDROID -static void -GetAndroidDeviceName(nsACString& aRetVal) -{ - nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); - MOZ_ASSERT(infoService, "Could not find a system info service"); - - Unused << NS_WARN_IF(NS_FAILED(infoService->GetPropertyAsACString( - NS_LITERAL_STRING("device"), aRetVal))); -} -#endif // MOZ_WIDGET_ANDROID - -} //anonymous namespace - -/** - * This wrapper is used to break circular-reference problem. - */ -class DNSServiceWrappedListener final - : public nsIDNSServiceDiscoveryListener - , public nsIDNSRegistrationListener - , public nsIDNSServiceResolveListener - , public nsIPresentationControlServerListener -{ -public: - NS_DECL_ISUPPORTS - NS_FORWARD_SAFE_NSIDNSSERVICEDISCOVERYLISTENER(mListener) - NS_FORWARD_SAFE_NSIDNSREGISTRATIONLISTENER(mListener) - NS_FORWARD_SAFE_NSIDNSSERVICERESOLVELISTENER(mListener) - NS_FORWARD_SAFE_NSIPRESENTATIONCONTROLSERVERLISTENER(mListener) - - explicit DNSServiceWrappedListener() = default; - - nsresult SetListener(MulticastDNSDeviceProvider* aListener) - { - mListener = aListener; - return NS_OK; - } - -private: - virtual ~DNSServiceWrappedListener() = default; - - MulticastDNSDeviceProvider* mListener = nullptr; -}; - -NS_IMPL_ISUPPORTS(DNSServiceWrappedListener, - nsIDNSServiceDiscoveryListener, - nsIDNSRegistrationListener, - nsIDNSServiceResolveListener, - nsIPresentationControlServerListener) - -NS_IMPL_ISUPPORTS(MulticastDNSDeviceProvider, - nsIPresentationDeviceProvider, - nsIDNSServiceDiscoveryListener, - nsIDNSRegistrationListener, - nsIDNSServiceResolveListener, - nsIPresentationControlServerListener, - nsIObserver) - -MulticastDNSDeviceProvider::~MulticastDNSDeviceProvider() -{ - Uninit(); -} - -nsresult -MulticastDNSDeviceProvider::Init() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mInitialized) { - return NS_OK; - } - - nsresult rv; - - mMulticastDNS = do_GetService(DNSSERVICEDISCOVERY_CONTRACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mWrappedListener = new DNSServiceWrappedListener(); - if (NS_WARN_IF(NS_FAILED(rv = mWrappedListener->SetListener(this)))) { - return rv; - } - - mPresentationService = do_CreateInstance(PRESENTATION_CONTROL_SERVICE_CONTACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mDiscoveryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mServerRetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - Preferences::AddStrongObservers(this, kObservedPrefs); - - mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY); - mDiscoveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS); - mDiscoverable = Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE); - mDiscoverableEncrypted = Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE_ENCRYPTED); - mServerRetryMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERABLE_RETRY_MS); - mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME); - -#ifdef MOZ_WIDGET_ANDROID - // FIXME: Bug 1185806 - Provide a common device name setting. - if (mServiceName.IsEmpty()) { - GetAndroidDeviceName(mServiceName); - Unused << Preferences::SetCString(PREF_PRESENTATION_DEVICE_NAME, mServiceName); - } -#endif // MOZ_WIDGET_ANDROID - - Unused << mPresentationService->SetId(mServiceName); - - if (mDiscoveryEnabled && NS_WARN_IF(NS_FAILED(rv = ForceDiscovery()))) { - return rv; - } - - if (mDiscoverable && NS_WARN_IF(NS_FAILED(rv = StartServer()))) { - return rv; - } - - mInitialized = true; - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::Uninit() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mInitialized) { - return NS_OK; - } - - ClearDevices(); - - Preferences::RemoveObservers(this, kObservedPrefs); - - StopDiscovery(NS_OK); - StopServer(); - - mMulticastDNS = nullptr; - - if (mWrappedListener) { - mWrappedListener->SetListener(nullptr); - mWrappedListener = nullptr; - } - - mInitialized = false; - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::StartServer() -{ - LOG_I("StartServer: %s (%d)", mServiceName.get(), mDiscoverable); - MOZ_ASSERT(NS_IsMainThread()); - - if (!mDiscoverable) { - return NS_OK; - } - - nsresult rv; - - uint16_t servicePort; - if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->GetPort(&servicePort)))) { - return rv; - } - - /** - * If |servicePort| is non-zero, it means PresentationControlService is running. - * Otherwise, we should make it start serving. - */ - if (servicePort) { - return RegisterMDNSService(); - } - - if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->SetListener(mWrappedListener)))) { - return rv; - } - - AbortServerRetry(); - - if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->StartServer(mDiscoverableEncrypted, 0)))) { - return rv; - } - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::StopServer() -{ - LOG_I("StopServer: %s", mServiceName.get()); - MOZ_ASSERT(NS_IsMainThread()); - - UnregisterMDNSService(NS_OK); - - AbortServerRetry(); - - if (mPresentationService) { - mPresentationService->SetListener(nullptr); - mPresentationService->Close(); - } - - return NS_OK; -} - -void -MulticastDNSDeviceProvider::AbortServerRetry() -{ - if (mIsServerRetrying) { - mIsServerRetrying = false; - mServerRetryTimer->Cancel(); - } -} - -nsresult -MulticastDNSDeviceProvider::RegisterMDNSService() -{ - LOG_I("RegisterMDNSService: %s", mServiceName.get()); - - if (!mDiscoverable) { - return NS_OK; - } - - // Cancel on going service registration. - UnregisterMDNSService(NS_OK); - - nsresult rv; - - uint16_t servicePort; - if (NS_FAILED(rv = mPresentationService->GetPort(&servicePort)) || - !servicePort) { - // Abort service registration if server port is not available. - return rv; - } - - /** - * Register the presentation control channel server as an mDNS service. - */ - nsCOMPtr<nsIDNSServiceInfo> serviceInfo = - do_CreateInstance(DNSSERVICEINFO_CONTRACT_ID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetServiceType( - NS_LITERAL_CSTRING(SERVICE_TYPE))))) { - return rv; - } - if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetServiceName(mServiceName)))) { - return rv; - } - if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetPort(servicePort)))) { - return rv; - } - - nsCOMPtr<nsIWritablePropertyBag2> propBag = - do_CreateInstance("@mozilla.org/hash-property-bag;1"); - MOZ_ASSERT(propBag); - - uint32_t version; - rv = mPresentationService->GetVersion(&version); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - rv = propBag->SetPropertyAsUint32(NS_LITERAL_STRING(PROTOCOL_VERSION_TAG), - version); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - if (mDiscoverableEncrypted) { - nsAutoCString certFingerprint; - rv = mPresentationService->GetCertFingerprint(certFingerprint); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - - rv = propBag->SetPropertyAsACString(NS_LITERAL_STRING(CERT_FINGERPRINT_TAG), - certFingerprint); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } - - if (NS_WARN_IF(NS_FAILED(rv = serviceInfo->SetAttributes(propBag)))) { - return rv; - } - - return mMulticastDNS->RegisterService(serviceInfo, - mWrappedListener, - getter_AddRefs(mRegisterRequest)); -} - -nsresult -MulticastDNSDeviceProvider::UnregisterMDNSService(nsresult aReason) -{ - LOG_I("UnregisterMDNSService: %s (0x%08x)", mServiceName.get(), aReason); - MOZ_ASSERT(NS_IsMainThread()); - - if (mRegisterRequest) { - mRegisterRequest->Cancel(aReason); - mRegisterRequest = nullptr; - } - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::StopDiscovery(nsresult aReason) -{ - LOG_I("StopDiscovery (0x%08x)", aReason); - - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDiscoveryTimer); - - Unused << mDiscoveryTimer->Cancel(); - - if (mDiscoveryRequest) { - mDiscoveryRequest->Cancel(aReason); - mDiscoveryRequest = nullptr; - } - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::Connect(Device* aDevice, - nsIPresentationControlChannel** aRetVal) -{ - MOZ_ASSERT(aDevice); - MOZ_ASSERT(mPresentationService); - - RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(), - aDevice->Address(), - aDevice->Port(), - aDevice->CertFingerprint()); - - return mPresentationService->Connect(deviceInfo, aRetVal); -} - -bool -MulticastDNSDeviceProvider::IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(aServiceInfo); - - nsCOMPtr<nsIPropertyBag2> propBag; - if (NS_WARN_IF(NS_FAILED( - aServiceInfo->GetAttributes(getter_AddRefs(propBag)))) || !propBag) { - return false; - } - - uint32_t remoteVersion; - if (NS_WARN_IF(NS_FAILED( - propBag->GetPropertyAsUint32(NS_LITERAL_STRING(PROTOCOL_VERSION_TAG), - &remoteVersion)))) { - return false; - } - - bool isCompatible = false; - Unused << NS_WARN_IF(NS_FAILED( - mPresentationService->IsCompatibleServer(remoteVersion, - &isCompatible))); - - return isCompatible; -} - -nsresult -MulticastDNSDeviceProvider::AddDevice(const nsACString& aId, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - RefPtr<Device> device = new Device(aId, /* ID */ - aServiceName, - aServiceType, - aAddress, - aPort, - aCertFingerprint, - DeviceState::eActive, - this); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->AddDevice(device); - } - - mDevices.AppendElement(device); - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::UpdateDevice(const uint32_t aIndex, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - if (NS_WARN_IF(aIndex >= mDevices.Length())) { - return NS_ERROR_INVALID_ARG; - } - - RefPtr<Device> device = mDevices[aIndex]; - device->Update(aServiceName, aServiceType, aAddress, aPort, aCertFingerprint); - device->ChangeState(DeviceState::eActive); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->UpdateDevice(device); - } - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::RemoveDevice(const uint32_t aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPresentationService); - - if (NS_WARN_IF(aIndex >= mDevices.Length())) { - return NS_ERROR_INVALID_ARG; - } - - RefPtr<Device> device = mDevices[aIndex]; - - LOG_I("RemoveDevice: %s", device->Id().get()); - mDevices.RemoveElementAt(aIndex); - - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->RemoveDevice(device); - } - - return NS_OK; -} - -bool -MulticastDNSDeviceProvider::FindDeviceById(const nsACString& aId, - uint32_t& aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - - RefPtr<Device> device = new Device(aId, - /* aName = */ EmptyCString(), - /* aType = */ EmptyCString(), - /* aHost = */ EmptyCString(), - /* aPort = */ 0, - /* aCertFingerprint */ EmptyCString(), - /* aState = */ DeviceState::eUnknown, - /* aProvider = */ nullptr); - size_t index = mDevices.IndexOf(device, 0, DeviceIdComparator()); - - if (index == mDevices.NoIndex) { - return false; - } - - aIndex = index; - return true; -} - -bool -MulticastDNSDeviceProvider::FindDeviceByAddress(const nsACString& aAddress, - uint32_t& aIndex) -{ - MOZ_ASSERT(NS_IsMainThread()); - - RefPtr<Device> device = new Device(/* aId = */ EmptyCString(), - /* aName = */ EmptyCString(), - /* aType = */ EmptyCString(), - aAddress, - /* aPort = */ 0, - /* aCertFingerprint */ EmptyCString(), - /* aState = */ DeviceState::eUnknown, - /* aProvider = */ nullptr); - size_t index = mDevices.IndexOf(device, 0, DeviceAddressComparator()); - - if (index == mDevices.NoIndex) { - return false; - } - - aIndex = index; - return true; -} - -void -MulticastDNSDeviceProvider::MarkAllDevicesUnknown() -{ - MOZ_ASSERT(NS_IsMainThread()); - - for (auto& device : mDevices) { - device->ChangeState(DeviceState::eUnknown); - } -} - -void -MulticastDNSDeviceProvider::ClearUnknownDevices() -{ - MOZ_ASSERT(NS_IsMainThread()); - - size_t i = mDevices.Length(); - while (i > 0) { - --i; - if (mDevices[i]->State() == DeviceState::eUnknown) { - Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i))); - } - } -} - -void -MulticastDNSDeviceProvider::ClearDevices() -{ - MOZ_ASSERT(NS_IsMainThread()); - - size_t i = mDevices.Length(); - while (i > 0) { - --i; - Unused << NS_WARN_IF(NS_FAILED(RemoveDevice(i))); - } -} - -// nsIPresentationDeviceProvider -NS_IMETHODIMP -MulticastDNSDeviceProvider::GetListener(nsIPresentationDeviceListener** aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aListener)) { - return NS_ERROR_INVALID_POINTER; - } - - nsresult rv; - nsCOMPtr<nsIPresentationDeviceListener> listener = - do_QueryReferent(mDeviceListener, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - listener.forget(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::SetListener(nsIPresentationDeviceListener* aListener) -{ - MOZ_ASSERT(NS_IsMainThread()); - - mDeviceListener = do_GetWeakReference(aListener); - - nsresult rv; - if (mDeviceListener) { - if (NS_WARN_IF(NS_FAILED(rv = Init()))) { - return rv; - } - } else { - if (NS_WARN_IF(NS_FAILED(rv = Uninit()))) { - return rv; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::ForceDiscovery() -{ - LOG_I("ForceDiscovery (%d)", mDiscoveryEnabled); - MOZ_ASSERT(NS_IsMainThread()); - - if (!mDiscoveryEnabled) { - return NS_OK; - } - - MOZ_ASSERT(mDiscoveryTimer); - MOZ_ASSERT(mMulticastDNS); - - // if it's already discovering, extend existing discovery timeout. - nsresult rv; - if (mIsDiscovering) { - Unused << mDiscoveryTimer->Cancel(); - - if (NS_WARN_IF(NS_FAILED( rv = mDiscoveryTimer->Init(this, - mDiscoveryTimeoutMs, - nsITimer::TYPE_ONE_SHOT)))) { - return rv; - } - return NS_OK; - } - - StopDiscovery(NS_OK); - - if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->StartDiscovery( - NS_LITERAL_CSTRING(SERVICE_TYPE), - mWrappedListener, - getter_AddRefs(mDiscoveryRequest))))) { - return rv; - } - - return NS_OK; -} - -// nsIDNSServiceDiscoveryListener -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType) -{ - LOG_I("OnDiscoveryStarted"); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDiscoveryTimer); - - MarkAllDevicesUnknown(); - - nsresult rv; - if (NS_WARN_IF(NS_FAILED(rv = mDiscoveryTimer->Init(this, - mDiscoveryTimeoutMs, - nsITimer::TYPE_ONE_SHOT)))) { - return rv; - } - - mIsDiscovering = true; - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnDiscoveryStopped(const nsACString& aServiceType) -{ - LOG_I("OnDiscoveryStopped"); - MOZ_ASSERT(NS_IsMainThread()); - - ClearUnknownDevices(); - - mIsDiscovering = false; - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServiceFound(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv ; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceFound: %s", serviceName.get()); - - if (mMulticastDNS) { - if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService( - aServiceInfo, mWrappedListener)))) { - return rv; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServiceLost(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceLost: %s", serviceName.get()); - - nsAutoCString host; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) { - return rv; - } - - uint32_t index; - if (!FindDeviceById(host, index)) { - // given device was not found - return NS_OK; - } - - if (NS_WARN_IF(NS_FAILED(rv = RemoveDevice(index)))) { - return rv; - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnStartDiscoveryFailed(const nsACString& aServiceType, - int32_t aErrorCode) -{ - LOG_E("OnStartDiscoveryFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnStopDiscoveryFailed(const nsACString& aServiceType, - int32_t aErrorCode) -{ - LOG_E("OnStopDiscoveryFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -// nsIDNSRegistrationListener -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServiceRegistered(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - nsresult rv; - - nsAutoCString name; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(name)))) { - return rv; - } - - LOG_I("OnServiceRegistered (%s)", name.get()); - mRegisteredName = name; - - if (mMulticastDNS) { - if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService( - aServiceInfo, mWrappedListener)))) { - return rv; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo) -{ - LOG_I("OnServiceUnregistered"); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnRegistrationFailed(nsIDNSServiceInfo* aServiceInfo, - int32_t aErrorCode) -{ - LOG_E("OnRegistrationFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - mRegisterRequest = nullptr; - - if (aErrorCode == nsIDNSRegistrationListener::ERROR_SERVICE_NOT_RUNNING) { - return NS_DispatchToMainThread( - NewRunnableMethod(this, &MulticastDNSDeviceProvider::RegisterMDNSService)); - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnUnregistrationFailed(nsIDNSServiceInfo* aServiceInfo, - int32_t aErrorCode) -{ - LOG_E("OnUnregistrationFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -// nsIDNSServiceResolveListener -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServiceResolved(nsIDNSServiceInfo* aServiceInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (NS_WARN_IF(!aServiceInfo)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv; - - nsAutoCString serviceName; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) { - return rv; - } - - LOG_I("OnServiceResolved: %s", serviceName.get()); - - nsAutoCString host; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) { - return rv; - } - - if (mRegisteredName == serviceName) { - LOG_I("ignore self"); - - if (NS_WARN_IF(NS_FAILED(rv = mPresentationService->SetId(host)))) { - return rv; - } - - return NS_OK; - } - - if (!IsCompatibleServer(aServiceInfo)) { - LOG_I("ignore incompatible service: %s", serviceName.get()); - return NS_OK; - } - - nsAutoCString address; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) { - return rv; - } - - uint16_t port; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) { - return rv; - } - - nsAutoCString serviceType; - if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceType(serviceType)))) { - return rv; - } - - nsCOMPtr<nsIPropertyBag2> propBag; - if (NS_WARN_IF(NS_FAILED( - aServiceInfo->GetAttributes(getter_AddRefs(propBag)))) || !propBag) { - return rv; - } - - nsAutoCString certFingerprint; - Unused << propBag->GetPropertyAsACString(NS_LITERAL_STRING(CERT_FINGERPRINT_TAG), - certFingerprint); - - uint32_t index; - if (FindDeviceById(host, index)) { - return UpdateDevice(index, - serviceName, - serviceType, - address, - port, - certFingerprint); - } else { - return AddDevice(host, - serviceName, - serviceType, - address, - port, - certFingerprint); - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo, - int32_t aErrorCode) -{ - LOG_E("OnResolveFailed: %d", aErrorCode); - MOZ_ASSERT(NS_IsMainThread()); - - return NS_OK; -} - -// nsIPresentationControlServerListener -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServerReady(uint16_t aPort, - const nsACString& aCertFingerprint) -{ - LOG_I("OnServerReady: %d, %s", aPort, PromiseFlatCString(aCertFingerprint).get()); - MOZ_ASSERT(NS_IsMainThread()); - - if (mDiscoverable) { - RegisterMDNSService(); - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnServerStopped(nsresult aResult) -{ - LOG_I("OnServerStopped: (0x%08x)", aResult); - - UnregisterMDNSService(aResult); - - // Try restart server if it is stopped abnormally. - if (NS_FAILED(aResult) && mDiscoverable) { - mIsServerRetrying = true; - mServerRetryTimer->Init(this, mServerRetryMs, nsITimer::TYPE_ONE_SHOT); - } - - return NS_OK; -} - -// Create a new device if we were unable to find one with the address. -already_AddRefed<MulticastDNSDeviceProvider::Device> -MulticastDNSDeviceProvider::GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo) -{ - nsAutoCString address; - Unused << aDeviceInfo->GetAddress(address); - - RefPtr<Device> device; - uint32_t index; - if (FindDeviceByAddress(address, index)) { - device = mDevices[index]; - } else { - // Create a one-time device object for non-discoverable controller. - // This device will not be in the list of available devices and cannot - // be used for requesting session. - nsAutoCString id; - Unused << aDeviceInfo->GetId(id); - uint16_t port; - Unused << aDeviceInfo->GetPort(&port); - - device = new Device(id, - /* aName = */ id, - /* aType = */ EmptyCString(), - address, - port, - /* aCertFingerprint */ EmptyCString(), - DeviceState::eActive, - /* aProvider = */ nullptr); - } - - return device.forget(); -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnSessionRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsAutoCString address; - Unused << aDeviceInfo->GetAddress(address); - - LOG_I("OnSessionRequest: %s", address.get()); - - RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo); - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->OnSessionRequest(device, aUrl, aPresentationId, - aControlChannel); - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnTerminateRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel, - bool aIsFromReceiver) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsAutoCString address; - Unused << aDeviceInfo->GetAddress(address); - - LOG_I("OnTerminateRequest: %s", address.get()); - - RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo); - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->OnTerminateRequest(device, aPresentationId, - aControlChannel, aIsFromReceiver); - } - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::OnReconnectRequest(nsITCPDeviceInfo* aDeviceInfo, - const nsAString& aUrl, - const nsAString& aPresentationId, - nsIPresentationControlChannel* aControlChannel) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsAutoCString address; - Unused << aDeviceInfo->GetAddress(address); - - LOG_I("OnReconnectRequest: %s", address.get()); - - RefPtr<Device> device = GetOrCreateDevice(aDeviceInfo); - nsCOMPtr<nsIPresentationDeviceListener> listener; - if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) { - Unused << listener->OnReconnectRequest(device, aUrl, aPresentationId, - aControlChannel); - } - - return NS_OK; -} - -// nsIObserver -NS_IMETHODIMP -MulticastDNSDeviceProvider::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - MOZ_ASSERT(NS_IsMainThread()); - - NS_ConvertUTF16toUTF8 data(aData); - LOG_I("Observe: topic = %s, data = %s", aTopic, data.get()); - - if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { - if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY)) { - OnDiscoveryChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERY)); - } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)) { - OnDiscoveryTimeoutChanged(Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS)); - } else if (data.EqualsLiteral(PREF_PRESENTATION_DISCOVERABLE)) { - OnDiscoverableChanged(Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE)); - } else if (data.EqualsLiteral(PREF_PRESENTATION_DEVICE_NAME)) { - nsAdoptingCString newServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME); - if (!mServiceName.Equals(newServiceName)) { - OnServiceNameChanged(newServiceName); - } - } - } else if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { - nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); - if (!timer) { - return NS_ERROR_UNEXPECTED; - } - - if (timer == mDiscoveryTimer) { - StopDiscovery(NS_OK); - } else if (timer == mServerRetryTimer) { - mIsServerRetrying = false; - StartServer(); - } - } - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::OnDiscoveryChanged(bool aEnabled) -{ - LOG_I("DiscoveryEnabled = %d\n", aEnabled); - MOZ_ASSERT(NS_IsMainThread()); - - mDiscoveryEnabled = aEnabled; - - if (mDiscoveryEnabled) { - return ForceDiscovery(); - } - - return StopDiscovery(NS_OK); -} - -nsresult -MulticastDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs) -{ - LOG_I("OnDiscoveryTimeoutChanged = %d\n", aTimeoutMs); - MOZ_ASSERT(NS_IsMainThread()); - - mDiscoveryTimeoutMs = aTimeoutMs; - - return NS_OK; -} - -nsresult -MulticastDNSDeviceProvider::OnDiscoverableChanged(bool aEnabled) -{ - LOG_I("Discoverable = %d\n", aEnabled); - MOZ_ASSERT(NS_IsMainThread()); - - mDiscoverable = aEnabled; - - if (mDiscoverable) { - return StartServer(); - } - - return StopServer(); -} - -nsresult -MulticastDNSDeviceProvider::OnServiceNameChanged(const nsACString& aServiceName) -{ - LOG_I("serviceName = %s\n", PromiseFlatCString(aServiceName).get()); - MOZ_ASSERT(NS_IsMainThread()); - - mServiceName = aServiceName; - - nsresult rv; - if (NS_WARN_IF(NS_FAILED(rv = UnregisterMDNSService(NS_OK)))) { - return rv; - } - - if (mDiscoverable) { - return RegisterMDNSService(); - } - - return NS_OK; -} - -// MulticastDNSDeviceProvider::Device -NS_IMPL_ISUPPORTS(MulticastDNSDeviceProvider::Device, - nsIPresentationDevice) - -// nsIPresentationDevice -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::GetId(nsACString& aId) -{ - aId = mId; - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::GetName(nsACString& aName) -{ - aName = mName; - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::GetType(nsACString& aType) -{ - aType = mType; - - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::EstablishControlChannel( - nsIPresentationControlChannel** aRetVal) -{ - if (!mProvider) { - return NS_ERROR_FAILURE; - } - - return mProvider->Connect(this, aRetVal); -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::Disconnect() -{ - // No need to do anything when disconnect. - return NS_OK; -} - -NS_IMETHODIMP -MulticastDNSDeviceProvider::Device::IsRequestedUrlSupported( - const nsAString& aRequestedUrl, - bool* aRetVal) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!aRetVal) { - return NS_ERROR_INVALID_POINTER; - } - - // TV 2.6 also supports presentation Apps and HTTP/HTTPS hosted receiver page. - if (DeviceProviderHelpers::IsFxTVSupportedAppUrl(aRequestedUrl) || - DeviceProviderHelpers::IsCommonlySupportedScheme(aRequestedUrl)) { - *aRetVal = true; - } - - return NS_OK; -} - -} // namespace presentation -} // namespace dom -} // namespace mozilla diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.h b/dom/presentation/provider/MulticastDNSDeviceProvider.h deleted file mode 100644 index c6a91b3d8..000000000 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.h +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- 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_presentation_provider_MulticastDNSDeviceProvider_h -#define mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h - -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "nsICancelable.h" -#include "nsIDNSServiceDiscovery.h" -#include "nsIObserver.h" -#include "nsIPresentationDevice.h" -#include "nsIPresentationDeviceProvider.h" -#include "nsIPresentationControlService.h" -#include "nsITimer.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsWeakPtr.h" - -class nsITCPDeviceInfo; - -namespace mozilla { -namespace dom { -namespace presentation { - -class DNSServiceWrappedListener; -class MulticastDNSService; - -class MulticastDNSDeviceProvider final - : public nsIPresentationDeviceProvider - , public nsIDNSServiceDiscoveryListener - , public nsIDNSRegistrationListener - , public nsIDNSServiceResolveListener - , public nsIPresentationControlServerListener - , public nsIObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICEPROVIDER - NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER - NS_DECL_NSIDNSREGISTRATIONLISTENER - NS_DECL_NSIDNSSERVICERESOLVELISTENER - NS_DECL_NSIPRESENTATIONCONTROLSERVERLISTENER - NS_DECL_NSIOBSERVER - - explicit MulticastDNSDeviceProvider() = default; - nsresult Init(); - nsresult Uninit(); - -private: - enum class DeviceState : uint32_t { - eUnknown, - eActive - }; - - class Device final : public nsIPresentationDevice - { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPRESENTATIONDEVICE - - explicit Device(const nsACString& aId, - const nsACString& aName, - const nsACString& aType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint, - DeviceState aState, - MulticastDNSDeviceProvider* aProvider) - : mId(aId) - , mName(aName) - , mType(aType) - , mAddress(aAddress) - , mPort(aPort) - , mCertFingerprint(aCertFingerprint) - , mState(aState) - , mProvider(aProvider) - { - } - - const nsCString& Id() const - { - return mId; - } - - const nsCString& Address() const - { - return mAddress; - } - - uint16_t Port() const - { - return mPort; - } - - const nsCString& CertFingerprint() const - { - return mCertFingerprint; - } - - DeviceState State() const - { - return mState; - } - - void ChangeState(DeviceState aState) - { - mState = aState; - } - - void Update(const nsACString& aName, - const nsACString& aType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint) - { - mName = aName; - mType = aType; - mAddress = aAddress; - mPort = aPort; - mCertFingerprint = aCertFingerprint; - } - - private: - virtual ~Device() = default; - - nsCString mId; - nsCString mName; - nsCString mType; - nsCString mAddress; - uint16_t mPort; - nsCString mCertFingerprint; - DeviceState mState; - MulticastDNSDeviceProvider* mProvider; - }; - - struct DeviceIdComparator { - bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const { - return aA->Id() == aB->Id(); - } - }; - - struct DeviceAddressComparator { - bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const { - return aA->Address() == aB->Address(); - } - }; - - virtual ~MulticastDNSDeviceProvider(); - nsresult StartServer(); - nsresult StopServer(); - void AbortServerRetry(); - nsresult RegisterMDNSService(); - nsresult UnregisterMDNSService(nsresult aReason); - nsresult StopDiscovery(nsresult aReason); - nsresult Connect(Device* aDevice, - nsIPresentationControlChannel** aRetVal); - bool IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo); - - // device manipulation - nsresult AddDevice(const nsACString& aId, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint); - nsresult UpdateDevice(const uint32_t aIndex, - const nsACString& aServiceName, - const nsACString& aServiceType, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint); - nsresult RemoveDevice(const uint32_t aIndex); - bool FindDeviceById(const nsACString& aId, - uint32_t& aIndex); - - bool FindDeviceByAddress(const nsACString& aAddress, - uint32_t& aIndex); - - already_AddRefed<Device> - GetOrCreateDevice(nsITCPDeviceInfo* aDeviceInfo); - - void MarkAllDevicesUnknown(); - void ClearUnknownDevices(); - void ClearDevices(); - - // preferences - nsresult OnDiscoveryChanged(bool aEnabled); - nsresult OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs); - nsresult OnDiscoverableChanged(bool aEnabled); - nsresult OnServiceNameChanged(const nsACString& aServiceName); - - bool mInitialized = false; - nsWeakPtr mDeviceListener; - nsCOMPtr<nsIPresentationControlService> mPresentationService; - nsCOMPtr<nsIDNSServiceDiscovery> mMulticastDNS; - RefPtr<DNSServiceWrappedListener> mWrappedListener; - - nsCOMPtr<nsICancelable> mDiscoveryRequest; - nsCOMPtr<nsICancelable> mRegisterRequest; - - nsTArray<RefPtr<Device>> mDevices; - - bool mDiscoveryEnabled = false; - bool mIsDiscovering = false; - uint32_t mDiscoveryTimeoutMs; - nsCOMPtr<nsITimer> mDiscoveryTimer; - - bool mDiscoverable = false; - bool mDiscoverableEncrypted = false; - bool mIsServerRetrying = false; - uint32_t mServerRetryMs; - nsCOMPtr<nsITimer> mServerRetryTimer; - - nsCString mServiceName; - nsCString mRegisteredName; -}; - -} // namespace presentation -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_presentation_provider_MulticastDNSDeviceProvider_h diff --git a/dom/presentation/provider/PresentationControlService.js b/dom/presentation/provider/PresentationControlService.js deleted file mode 100644 index e9f92247f..000000000 --- a/dom/presentation/provider/PresentationControlService.js +++ /dev/null @@ -1,960 +0,0 @@ -/* 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components, dump */ -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -/* globals XPCOMUtils */ -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -/* globals Services */ -Cu.import("resource://gre/modules/Services.jsm"); -/* globals NetUtil */ -Cu.import("resource://gre/modules/NetUtil.jsm"); -/* globals setTimeout, clearTimeout */ -Cu.import("resource://gre/modules/Timer.jsm"); - -/* globals ControllerStateMachine */ -XPCOMUtils.defineLazyModuleGetter(this, "ControllerStateMachine", // jshint ignore:line - "resource://gre/modules/presentation/ControllerStateMachine.jsm"); -/* global ReceiverStateMachine */ -XPCOMUtils.defineLazyModuleGetter(this, "ReceiverStateMachine", // jshint ignore:line - "resource://gre/modules/presentation/ReceiverStateMachine.jsm"); - -const kProtocolVersion = 1; // need to review isCompatibleServer while fiddling the version number. -const kLocalCertName = "presentation"; - -const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug"); -function log(aMsg) { - dump("-*- PresentationControlService.js: " + aMsg + "\n"); -} - -function TCPDeviceInfo(aAddress, aPort, aId, aCertFingerprint) { - this.address = aAddress; - this.port = aPort; - this.id = aId; - this.certFingerprint = aCertFingerprint || ""; -} - -function PresentationControlService() { - this._id = null; - this._port = 0; - this._serverSocket = null; -} - -PresentationControlService.prototype = { - /** - * If a user agent connects to this server, we create a control channel but - * hand it to |TCPDevice.listener| when the initial information exchange - * finishes. Therefore, we hold the control channels in this period. - */ - _controlChannels: [], - - startServer: function(aEncrypted, aPort) { - if (this._isServiceInit()) { - DEBUG && log("PresentationControlService - server socket has been initialized"); // jshint ignore:line - throw Cr.NS_ERROR_FAILURE; - } - - /** - * 0 or undefined indicates opt-out parameter, and a port will be selected - * automatically. - */ - let serverSocketPort = (typeof aPort !== "undefined" && aPort !== 0) ? aPort : -1; - - if (aEncrypted) { - let self = this; - let localCertService = Cc["@mozilla.org/security/local-cert-service;1"] - .getService(Ci.nsILocalCertService); - localCertService.getOrCreateCert(kLocalCertName, { - handleCert: function(aCert, aRv) { - DEBUG && log("PresentationControlService - handleCert"); // jshint ignore:line - if (aRv) { - self._notifyServerStopped(aRv); - } else { - self._serverSocket = Cc["@mozilla.org/network/tls-server-socket;1"] - .createInstance(Ci.nsITLSServerSocket); - - self._serverSocketInit(serverSocketPort, aCert); - } - } - }); - } else { - this._serverSocket = Cc["@mozilla.org/network/server-socket;1"] - .createInstance(Ci.nsIServerSocket); - - this._serverSocketInit(serverSocketPort, null); - } - }, - - _serverSocketInit: function(aPort, aCert) { - if (!this._serverSocket) { - DEBUG && log("PresentationControlService - create server socket fail."); // jshint ignore:line - throw Cr.NS_ERROR_FAILURE; - } - - try { - this._serverSocket.init(aPort, false, -1); - - if (aCert) { - this._serverSocket.serverCert = aCert; - this._serverSocket.setSessionTickets(false); - let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER; - this._serverSocket.setRequestClientCertificate(requestCert); - } - - this._serverSocket.asyncListen(this); - } catch (e) { - // NS_ERROR_SOCKET_ADDRESS_IN_USE - DEBUG && log("PresentationControlService - init server socket fail: " + e); // jshint ignore:line - throw Cr.NS_ERROR_FAILURE; - } - - this._port = this._serverSocket.port; - - DEBUG && log("PresentationControlService - service start on port: " + this._port); // jshint ignore:line - - // Monitor network interface change to restart server socket. - Services.obs.addObserver(this, "network:offline-status-changed", false); - - this._notifyServerReady(); - }, - - _notifyServerReady: function() { - Services.tm.mainThread.dispatch(() => { - if (this._listener) { - this._listener.onServerReady(this._port, this.certFingerprint); - } - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - - _notifyServerStopped: function(aRv) { - Services.tm.mainThread.dispatch(() => { - if (this._listener) { - this._listener.onServerStopped(aRv); - } - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - - isCompatibleServer: function(aVersion) { - // No compatibility issue for the first version of control protocol - return this.version === aVersion; - }, - - get id() { - return this._id; - }, - - set id(aId) { - this._id = aId; - }, - - get port() { - return this._port; - }, - - get version() { - return kProtocolVersion; - }, - - get certFingerprint() { - if (!this._serverSocket.serverCert) { - return null; - } - - return this._serverSocket.serverCert.sha256Fingerprint; - }, - - set listener(aListener) { - this._listener = aListener; - }, - - get listener() { - return this._listener; - }, - - _isServiceInit: function() { - return this._serverSocket !== null; - }, - - connect: function(aDeviceInfo) { - if (!this.id) { - DEBUG && log("PresentationControlService - Id has not initialized; connect fails"); // jshint ignore:line - return null; - } - DEBUG && log("PresentationControlService - connect to " + aDeviceInfo.id); // jshint ignore:line - - let socketTransport = this._attemptConnect(aDeviceInfo); - return new TCPControlChannel(this, - socketTransport, - aDeviceInfo, - "sender"); - }, - - _attemptConnect: function(aDeviceInfo) { - let sts = Cc["@mozilla.org/network/socket-transport-service;1"] - .getService(Ci.nsISocketTransportService); - - let socketTransport; - try { - if (aDeviceInfo.certFingerprint) { - let overrideService = Cc["@mozilla.org/security/certoverride;1"] - .getService(Ci.nsICertOverrideService); - overrideService.rememberTemporaryValidityOverrideUsingFingerprint( - aDeviceInfo.address, - aDeviceInfo.port, - aDeviceInfo.certFingerprint, - Ci.nsICertOverrideService.ERROR_UNTRUSTED | Ci.nsICertOverrideService.ERROR_MISMATCH); - - socketTransport = sts.createTransport(["ssl"], - 1, - aDeviceInfo.address, - aDeviceInfo.port, - null); - } else { - socketTransport = sts.createTransport(null, - 0, - aDeviceInfo.address, - aDeviceInfo.port, - null); - } - // Shorten the connection failure procedure. - socketTransport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 2); - } catch (e) { - DEBUG && log("PresentationControlService - createTransport throws: " + e); // jshint ignore:line - // Pop the exception to |TCPDevice.establishControlChannel| - throw Cr.NS_ERROR_FAILURE; - } - return socketTransport; - }, - - responseSession: function(aDeviceInfo, aSocketTransport) { - if (!this._isServiceInit()) { - DEBUG && log("PresentationControlService - should never receive remote " + - "session request before server socket initialization"); // jshint ignore:line - return null; - } - DEBUG && log("PresentationControlService - responseSession to " + - JSON.stringify(aDeviceInfo)); // jshint ignore:line - return new TCPControlChannel(this, - aSocketTransport, - aDeviceInfo, - "receiver"); - }, - - // Triggered by TCPControlChannel - onSessionRequest: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) { - DEBUG && log("PresentationControlService - onSessionRequest: " + - aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line - if (!this.listener) { - this.releaseControlChannel(aControlChannel); - return; - } - - this.listener.onSessionRequest(aDeviceInfo, - aUrl, - aPresentationId, - aControlChannel); - this.releaseControlChannel(aControlChannel); - }, - - onSessionTerminate: function(aDeviceInfo, aPresentationId, aControlChannel, aIsFromReceiver) { - DEBUG && log("TCPPresentationServer - onSessionTerminate: " + - aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line - if (!this.listener) { - this.releaseControlChannel(aControlChannel); - return; - } - - this.listener.onTerminateRequest(aDeviceInfo, - aPresentationId, - aControlChannel, - aIsFromReceiver); - this.releaseControlChannel(aControlChannel); - }, - - onSessionReconnect: function(aDeviceInfo, aUrl, aPresentationId, aControlChannel) { - DEBUG && log("TCPPresentationServer - onSessionReconnect: " + - aDeviceInfo.address + ":" + aDeviceInfo.port); // jshint ignore:line - if (!this.listener) { - this.releaseControlChannel(aControlChannel); - return; - } - - this.listener.onReconnectRequest(aDeviceInfo, - aUrl, - aPresentationId, - aControlChannel); - this.releaseControlChannel(aControlChannel); - }, - - // nsIServerSocketListener (Triggered by nsIServerSocket.init) - onSocketAccepted: function(aServerSocket, aClientSocket) { - DEBUG && log("PresentationControlService - onSocketAccepted: " + - aClientSocket.host + ":" + aClientSocket.port); // jshint ignore:line - let deviceInfo = new TCPDeviceInfo(aClientSocket.host, aClientSocket.port); - this.holdControlChannel(this.responseSession(deviceInfo, aClientSocket)); - }, - - holdControlChannel: function(aControlChannel) { - this._controlChannels.push(aControlChannel); - }, - - releaseControlChannel: function(aControlChannel) { - let index = this._controlChannels.indexOf(aControlChannel); - if (index !== -1) { - delete this._controlChannels[index]; - } - }, - - // nsIServerSocketListener (Triggered by nsIServerSocket.init) - onStopListening: function(aServerSocket, aStatus) { - DEBUG && log("PresentationControlService - onStopListening: " + aStatus); // jshint ignore:line - }, - - close: function() { - DEBUG && log("PresentationControlService - close"); // jshint ignore:line - if (this._isServiceInit()) { - DEBUG && log("PresentationControlService - close server socket"); // jshint ignore:line - this._serverSocket.close(); - this._serverSocket = null; - - Services.obs.removeObserver(this, "network:offline-status-changed"); - - this._notifyServerStopped(Cr.NS_OK); - } - this._port = 0; - }, - - // nsIObserver - observe: function(aSubject, aTopic, aData) { - DEBUG && log("PresentationControlService - observe: " + aTopic); // jshint ignore:line - switch (aTopic) { - case "network:offline-status-changed": { - if (aData == "offline") { - DEBUG && log("network offline"); // jshint ignore:line - return; - } - this._restartServer(); - break; - } - } - }, - - _restartServer: function() { - DEBUG && log("PresentationControlService - restart service"); // jshint ignore:line - - // restart server socket - if (this._isServiceInit()) { - this.close(); - - try { - this.startServer(); - } catch (e) { - DEBUG && log("PresentationControlService - restart service fail: " + e); // jshint ignore:line - } - } - }, - - classID: Components.ID("{f4079b8b-ede5-4b90-a112-5b415a931deb}"), - QueryInterface : XPCOMUtils.generateQI([Ci.nsIServerSocketListener, - Ci.nsIPresentationControlService, - Ci.nsIObserver]), -}; - -function ChannelDescription(aInit) { - this._type = aInit.type; - switch (this._type) { - case Ci.nsIPresentationChannelDescription.TYPE_TCP: - this._tcpAddresses = Cc["@mozilla.org/array;1"] - .createInstance(Ci.nsIMutableArray); - for (let address of aInit.tcpAddress) { - let wrapper = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); - wrapper.data = address; - this._tcpAddresses.appendElement(wrapper, false); - } - - this._tcpPort = aInit.tcpPort; - break; - case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL: - this._dataChannelSDP = aInit.dataChannelSDP; - break; - } -} - -ChannelDescription.prototype = { - _type: 0, - _tcpAddresses: null, - _tcpPort: 0, - _dataChannelSDP: "", - - get type() { - return this._type; - }, - - get tcpAddress() { - return this._tcpAddresses; - }, - - get tcpPort() { - return this._tcpPort; - }, - - get dataChannelSDP() { - return this._dataChannelSDP; - }, - - classID: Components.ID("{82507aea-78a2-487e-904a-858a6c5bf4e1}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), -}; - -// Helper function: transfer nsIPresentationChannelDescription to json -function discriptionAsJson(aDescription) { - let json = {}; - json.type = aDescription.type; - switch(aDescription.type) { - case Ci.nsIPresentationChannelDescription.TYPE_TCP: - let addresses = aDescription.tcpAddress.QueryInterface(Ci.nsIArray); - json.tcpAddress = []; - for (let idx = 0; idx < addresses.length; idx++) { - let address = addresses.queryElementAt(idx, Ci.nsISupportsCString); - json.tcpAddress.push(address.data); - } - json.tcpPort = aDescription.tcpPort; - break; - case Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL: - json.dataChannelSDP = aDescription.dataChannelSDP; - break; - } - return json; -} - -const kDisconnectTimeout = 5000; -const kTerminateTimeout = 5000; - -function TCPControlChannel(presentationService, - transport, - deviceInfo, - direction) { - DEBUG && log("create TCPControlChannel for : " + direction); // jshint ignore:line - this._deviceInfo = deviceInfo; - this._direction = direction; - this._transport = transport; - - this._presentationService = presentationService; - - if (direction === "receiver") { - // Need to set security observer before I/O stream operation. - this._setSecurityObserver(this); - } - - let currentThread = Services.tm.currentThread; - transport.setEventSink(this, currentThread); - - this._input = this._transport.openInputStream(0, 0, 0) - .QueryInterface(Ci.nsIAsyncInputStream); - this._input.asyncWait(this.QueryInterface(Ci.nsIStreamListener), - Ci.nsIAsyncInputStream.WAIT_CLOSURE_ONLY, - 0, - currentThread); - - this._output = this._transport - .openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0) - .QueryInterface(Ci.nsIAsyncOutputStream); - - this._outgoingMsgs = []; - - - this._stateMachine = - (direction === "sender") ? new ControllerStateMachine(this, presentationService.id) - : new ReceiverStateMachine(this); - - if (direction === "receiver" && !transport.securityInfo) { - // Since the transport created by server socket is already CONNECTED_TO. - this._outgoingEnabled = true; - this._createInputStreamPump(); - } -} - -TCPControlChannel.prototype = { - _outgoingEnabled: false, - _incomingEnabled: false, - _pendingOpen: false, - _pendingOffer: null, - _pendingAnswer: null, - _pendingClose: null, - _pendingCloseReason: null, - _pendingReconnect: false, - - sendOffer: function(aOffer) { - this._stateMachine.sendOffer(discriptionAsJson(aOffer)); - }, - - sendAnswer: function(aAnswer) { - this._stateMachine.sendAnswer(discriptionAsJson(aAnswer)); - }, - - sendIceCandidate: function(aCandidate) { - this._stateMachine.updateIceCandidate(aCandidate); - }, - - launch: function(aPresentationId, aUrl) { - this._stateMachine.launch(aPresentationId, aUrl); - }, - - terminate: function(aPresentationId) { - if (!this._terminatingId) { - this._terminatingId = aPresentationId; - this._stateMachine.terminate(aPresentationId); - - // Start a guard timer to ensure terminateAck is processed. - this._terminateTimer = setTimeout(() => { - DEBUG && log("TCPControlChannel - terminate timeout: " + aPresentationId); // jshint ignore:line - delete this._terminateTimer; - if (this._pendingDisconnect) { - this._pendingDisconnect(); - } else { - this.disconnect(Cr.NS_OK); - } - }, kTerminateTimeout); - } else { - this._stateMachine.terminateAck(aPresentationId); - delete this._terminatingId; - } - }, - - _flushOutgoing: function() { - if (!this._outgoingEnabled || this._outgoingMsgs.length === 0) { - return; - } - - this._output.asyncWait(this, 0, 0, Services.tm.currentThread); - }, - - // may throw an exception - _send: function(aMsg) { - DEBUG && log("TCPControlChannel - Send: " + JSON.stringify(aMsg, null, 2)); // jshint ignore:line - - /** - * XXX In TCP streaming, it is possible that more than one message in one - * TCP packet. We use line delimited JSON to identify where one JSON encoded - * object ends and the next begins. Therefore, we do not allow newline - * characters whithin the whole message, and add a newline at the end. - * Please see the parser code in |onDataAvailable|. - */ - let message = JSON.stringify(aMsg).replace(["\n"], "") + "\n"; - try { - this._output.write(message, message.length); - } catch(e) { - DEBUG && log("TCPControlChannel - Failed to send message: " + e.name); // jshint ignore:line - throw e; - } - }, - - _setSecurityObserver: function(observer) { - if (this._transport && this._transport.securityInfo) { - DEBUG && log("TCPControlChannel - setSecurityObserver: " + observer); // jshint ignore:line - let connectionInfo = this._transport.securityInfo - .QueryInterface(Ci.nsITLSServerConnectionInfo); - connectionInfo.setSecurityObserver(observer); - } - }, - - // nsITLSServerSecurityObserver - onHandshakeDone: function(socket, clientStatus) { - log("TCPControlChannel - onHandshakeDone: TLS version: " + clientStatus.tlsVersionUsed.toString(16)); - this._setSecurityObserver(null); - - // Process input/output after TLS handshake is complete. - this._outgoingEnabled = true; - this._createInputStreamPump(); - }, - - // nsIAsyncOutputStream - onOutputStreamReady: function() { - DEBUG && log("TCPControlChannel - onOutputStreamReady"); // jshint ignore:line - if (this._outgoingMsgs.length === 0) { - return; - } - - try { - this._send(this._outgoingMsgs[0]); - } catch (e) { - if (e.result === Cr.NS_BASE_STREAM_WOULD_BLOCK) { - this._output.asyncWait(this, 0, 0, Services.tm.currentThread); - return; - } - - this._closeTransport(); - return; - } - this._outgoingMsgs.shift(); - this._flushOutgoing(); - }, - - // nsIAsyncInputStream (Triggered by nsIInputStream.asyncWait) - // Only used for detecting connection refused - onInputStreamReady: function(aStream) { - DEBUG && log("TCPControlChannel - onInputStreamReady"); // jshint ignore:line - try { - aStream.available(); - } catch (e) { - DEBUG && log("TCPControlChannel - onInputStreamReady error: " + e.name); // jshint ignore:line - // NS_ERROR_CONNECTION_REFUSED - this._notifyDisconnected(e.result); - } - }, - - // nsITransportEventSink (Triggered by nsISocketTransport.setEventSink) - onTransportStatus: function(aTransport, aStatus) { - DEBUG && log("TCPControlChannel - onTransportStatus: " + aStatus.toString(16) + - " with role: " + this._direction); // jshint ignore:line - if (aStatus === Ci.nsISocketTransport.STATUS_CONNECTED_TO) { - this._outgoingEnabled = true; - this._createInputStreamPump(); - } - }, - - // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead) - onStartRequest: function() { - DEBUG && log("TCPControlChannel - onStartRequest with role: " + - this._direction); // jshint ignore:line - this._incomingEnabled = true; - }, - - // nsIRequestObserver (Triggered by nsIInputStreamPump.asyncRead) - onStopRequest: function(aRequest, aContext, aStatus) { - DEBUG && log("TCPControlChannel - onStopRequest: " + aStatus + - " with role: " + this._direction); // jshint ignore:line - this._stateMachine.onChannelClosed(aStatus, true); - }, - - // nsIStreamListener (Triggered by nsIInputStreamPump.asyncRead) - onDataAvailable: function(aRequest, aContext, aInputStream) { - let data = NetUtil.readInputStreamToString(aInputStream, - aInputStream.available()); - DEBUG && log("TCPControlChannel - onDataAvailable: " + data); // jshint ignore:line - - // Parser of line delimited JSON. Please see |_send| for more informaiton. - let jsonArray = data.split("\n"); - jsonArray.pop(); - for (let json of jsonArray) { - let msg; - try { - msg = JSON.parse(json); - } catch (e) { - DEBUG && log("TCPSignalingChannel - error in parsing json: " + e); // jshint ignore:line - } - - this._handleMessage(msg); - } - }, - - _createInputStreamPump: function() { - if (this._pump) { - return; - } - - DEBUG && log("TCPControlChannel - create pump with role: " + - this._direction); // jshint ignore:line - this._pump = Cc["@mozilla.org/network/input-stream-pump;1"]. - createInstance(Ci.nsIInputStreamPump); - this._pump.init(this._input, -1, -1, 0, 0, false); - this._pump.asyncRead(this, null); - this._stateMachine.onChannelReady(); - }, - - // Handle command from remote side - _handleMessage: function(aMsg) { - DEBUG && log("TCPControlChannel - handleMessage from " + - JSON.stringify(this._deviceInfo) + ": " + JSON.stringify(aMsg)); // jshint ignore:line - this._stateMachine.onCommand(aMsg); - }, - - get listener() { - return this._listener; - }, - - set listener(aListener) { - DEBUG && log("TCPControlChannel - set listener: " + aListener); // jshint ignore:line - if (!aListener) { - this._listener = null; - return; - } - - this._listener = aListener; - if (this._pendingOpen) { - this._pendingOpen = false; - DEBUG && log("TCPControlChannel - notify pending opened"); // jshint ignore:line - this._listener.notifyConnected(); - } - - if (this._pendingOffer) { - let offer = this._pendingOffer; - DEBUG && log("TCPControlChannel - notify pending offer: " + - JSON.stringify(offer)); // jshint ignore:line - this._listener.onOffer(new ChannelDescription(offer)); - this._pendingOffer = null; - } - - if (this._pendingAnswer) { - let answer = this._pendingAnswer; - DEBUG && log("TCPControlChannel - notify pending answer: " + - JSON.stringify(answer)); // jshint ignore:line - this._listener.onAnswer(new ChannelDescription(answer)); - this._pendingAnswer = null; - } - - if (this._pendingClose) { - DEBUG && log("TCPControlChannel - notify pending closed"); // jshint ignore:line - this._notifyDisconnected(this._pendingCloseReason); - this._pendingClose = null; - } - - if (this._pendingReconnect) { - DEBUG && log("TCPControlChannel - notify pending reconnected"); // jshint ignore:line - this._notifyReconnected(); - this._pendingReconnect = false; - } - }, - - /** - * These functions are designed to handle the interaction with listener - * appropriately. |_FUNC| is to handle |this._listener.FUNC|. - */ - _onOffer: function(aOffer) { - if (!this._incomingEnabled) { - return; - } - if (!this._listener) { - this._pendingOffer = aOffer; - return; - } - DEBUG && log("TCPControlChannel - notify offer: " + - JSON.stringify(aOffer)); // jshint ignore:line - this._listener.onOffer(new ChannelDescription(aOffer)); - }, - - _onAnswer: function(aAnswer) { - if (!this._incomingEnabled) { - return; - } - if (!this._listener) { - this._pendingAnswer = aAnswer; - return; - } - DEBUG && log("TCPControlChannel - notify answer: " + - JSON.stringify(aAnswer)); // jshint ignore:line - this._listener.onAnswer(new ChannelDescription(aAnswer)); - }, - - _notifyConnected: function() { - this._pendingClose = false; - this._pendingCloseReason = Cr.NS_OK; - - if (!this._listener) { - this._pendingOpen = true; - return; - } - - DEBUG && log("TCPControlChannel - notify opened with role: " + - this._direction); // jshint ignore:line - this._listener.notifyConnected(); - }, - - _notifyDisconnected: function(aReason) { - this._pendingOpen = false; - this._pendingOffer = null; - this._pendingAnswer = null; - - // Remote endpoint closes the control channel with abnormal reason. - if (aReason == Cr.NS_OK && this._pendingCloseReason != Cr.NS_OK) { - aReason = this._pendingCloseReason; - } - - if (!this._listener) { - this._pendingClose = true; - this._pendingCloseReason = aReason; - return; - } - - DEBUG && log("TCPControlChannel - notify closed with role: " + - this._direction); // jshint ignore:line - this._listener.notifyDisconnected(aReason); - }, - - _notifyReconnected: function() { - if (!this._listener) { - this._pendingReconnect = true; - return; - } - - DEBUG && log("TCPControlChannel - notify reconnected with role: " + - this._direction); // jshint ignore:line - this._listener.notifyReconnected(); - }, - - _closeOutgoing: function() { - if (this._outgoingEnabled) { - this._output.close(); - this._outgoingEnabled = false; - } - }, - _closeIncoming: function() { - if (this._incomingEnabled) { - this._pump = null; - this._input.close(); - this._incomingEnabled = false; - } - }, - _closeTransport: function() { - if (this._disconnectTimer) { - clearTimeout(this._disconnectTimer); - delete this._disconnectTimer; - } - - if (this._terminateTimer) { - clearTimeout(this._terminateTimer); - delete this._terminateTimer; - } - - delete this._pendingDisconnect; - - this._transport.setEventSink(null, null); - - this._closeIncoming(); - this._closeOutgoing(); - this._presentationService.releaseControlChannel(this); - }, - - disconnect: function(aReason) { - DEBUG && log("TCPControlChannel - disconnect with reason: " + aReason); // jshint ignore:line - - // Pending disconnect during termination procedure. - if (this._terminateTimer) { - // Store only the first disconnect action. - if (!this._pendingDisconnect) { - this._pendingDisconnect = this.disconnect.bind(this, aReason); - } - return; - } - - if (this._outgoingEnabled && !this._disconnectTimer) { - // default reason is NS_OK - aReason = !aReason ? Cr.NS_OK : aReason; - - this._stateMachine.onChannelClosed(aReason, false); - - // Start a guard timer to ensure the transport will be closed. - this._disconnectTimer = setTimeout(() => { - DEBUG && log("TCPControlChannel - disconnect timeout"); // jshint ignore:line - this._closeTransport(); - }, kDisconnectTimeout); - } - }, - - reconnect: function(aPresentationId, aUrl) { - DEBUG && log("TCPControlChannel - reconnect with role: " + - this._direction); // jshint ignore:line - if (this._direction != "sender") { - return Cr.NS_ERROR_FAILURE; - } - - this._stateMachine.reconnect(aPresentationId, aUrl); - }, - - // callback from state machine - sendCommand: function(command) { - this._outgoingMsgs.push(command); - this._flushOutgoing(); - }, - - notifyDeviceConnected: function(deviceId) { - switch (this._direction) { - case "receiver": - this._deviceInfo.id = deviceId; - break; - } - this._notifyConnected(); - }, - - notifyDisconnected: function(reason) { - this._closeTransport(); - this._notifyDisconnected(reason); - }, - - notifyLaunch: function(presentationId, url) { - switch (this._direction) { - case "receiver": - this._presentationService.onSessionRequest(this._deviceInfo, - url, - presentationId, - this); - break; - } - }, - - notifyTerminate: function(presentationId) { - if (!this._terminatingId) { - this._terminatingId = presentationId; - this._presentationService.onSessionTerminate(this._deviceInfo, - presentationId, - this, - this._direction === "sender"); - return; - } - - // Cancel terminate guard timer after receiving terminate-ack. - if (this._terminateTimer) { - clearTimeout(this._terminateTimer); - delete this._terminateTimer; - } - - if (this._terminatingId !== presentationId) { - // Requested presentation Id doesn't matched with the one in ACK. - // Disconnect the control channel with error. - DEBUG && log("TCPControlChannel - unmatched terminatingId: " + presentationId); // jshint ignore:line - this.disconnect(Cr.NS_ERROR_FAILURE); - } - - delete this._terminatingId; - if (this._pendingDisconnect) { - this._pendingDisconnect(); - } - }, - - notifyReconnect: function(presentationId, url) { - switch (this._direction) { - case "receiver": - this._presentationService.onSessionReconnect(this._deviceInfo, - url, - presentationId, - this); - break; - case "sender": - this._notifyReconnected(); - break; - } - }, - - notifyOffer: function(offer) { - this._onOffer(offer); - }, - - notifyAnswer: function(answer) { - this._onAnswer(answer); - }, - - notifyIceCandidate: function(candidate) { - this._listener.onIceCandidate(candidate); - }, - - classID: Components.ID("{fefb8286-0bdc-488b-98bf-0c11b485c955}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel, - Ci.nsIStreamListener]), -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationControlService]); // jshint ignore:line diff --git a/dom/presentation/provider/PresentationDeviceProviderModule.cpp b/dom/presentation/provider/PresentationDeviceProviderModule.cpp deleted file mode 100644 index 9c084e7db..000000000 --- a/dom/presentation/provider/PresentationDeviceProviderModule.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- 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 "DisplayDeviceProvider.h" -#include "MulticastDNSDeviceProvider.h" -#include "mozilla/ModuleUtils.h" - -#ifdef MOZ_WIDGET_ANDROID -#include "LegacyMDNSDeviceProvider.h" -#endif //MOZ_WIDGET_ANDROID - -#define MULTICAST_DNS_PROVIDER_CID \ - {0x814f947a, 0x52f7, 0x41c9, \ - { 0x94, 0xa1, 0x36, 0x84, 0x79, 0x72, 0x84, 0xac }} -#define DISPLAY_DEVICE_PROVIDER_CID \ - { 0x515d9879, 0xfe0b, 0x4d9f, \ - { 0x89, 0x49, 0x7f, 0xa7, 0x65, 0x6c, 0x01, 0x0e } } - -#ifdef MOZ_WIDGET_ANDROID -#define LEGACY_MDNS_PROVIDER_CID \ - { 0x6885ff39, 0xd98c, 0x4356, \ - { 0x9e, 0xb3, 0x56, 0x56, 0x31, 0x63, 0x0a, 0xf6 } } -#endif //MOZ_WIDGET_ANDROID - -#define DISPLAY_DEVICE_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/displaydevice-provider;1" -#define MULTICAST_DNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/multicastdns-provider;1" - -#ifdef MOZ_WIDGET_ANDROID -#define LEGACY_MDNS_PROVIDER_CONTRACT_ID "@mozilla.org/presentation-device/legacy-mdns-provider;1" -#endif //MOZ_WIDGET_ANDROID - -using mozilla::dom::presentation::MulticastDNSDeviceProvider; -using mozilla::dom::presentation::DisplayDeviceProvider; - -#ifdef MOZ_WIDGET_ANDROID -using mozilla::dom::presentation::legacy::LegacyMDNSDeviceProvider; -#endif //MOZ_WIDGET_ANDROID - -NS_GENERIC_FACTORY_CONSTRUCTOR(MulticastDNSDeviceProvider) -NS_DEFINE_NAMED_CID(MULTICAST_DNS_PROVIDER_CID); - -NS_GENERIC_FACTORY_CONSTRUCTOR(DisplayDeviceProvider) -NS_DEFINE_NAMED_CID(DISPLAY_DEVICE_PROVIDER_CID); - -#ifdef MOZ_WIDGET_ANDROID -NS_GENERIC_FACTORY_CONSTRUCTOR(LegacyMDNSDeviceProvider) -NS_DEFINE_NAMED_CID(LEGACY_MDNS_PROVIDER_CID); -#endif //MOZ_WIDGET_ANDROID - -static const mozilla::Module::CIDEntry kPresentationDeviceProviderCIDs[] = { - { &kMULTICAST_DNS_PROVIDER_CID, false, nullptr, MulticastDNSDeviceProviderConstructor }, - { &kDISPLAY_DEVICE_PROVIDER_CID, false, nullptr, DisplayDeviceProviderConstructor }, -#ifdef MOZ_WIDGET_ANDROID - { &kLEGACY_MDNS_PROVIDER_CID, false, nullptr, LegacyMDNSDeviceProviderConstructor }, -#endif //MOZ_WIDGET_ANDROID - { nullptr } -}; - -static const mozilla::Module::ContractIDEntry kPresentationDeviceProviderContracts[] = { - { MULTICAST_DNS_PROVIDER_CONTRACT_ID, &kMULTICAST_DNS_PROVIDER_CID }, - { DISPLAY_DEVICE_PROVIDER_CONTRACT_ID, &kDISPLAY_DEVICE_PROVIDER_CID }, -#ifdef MOZ_WIDGET_ANDROID - { LEGACY_MDNS_PROVIDER_CONTRACT_ID, &kLEGACY_MDNS_PROVIDER_CID }, -#endif //MOZ_WIDGET_ANDROID - { nullptr } -}; - -static const mozilla::Module::CategoryEntry kPresentationDeviceProviderCategories[] = { -#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_ANDROID) - { PRESENTATION_DEVICE_PROVIDER_CATEGORY, "MulticastDNSDeviceProvider", MULTICAST_DNS_PROVIDER_CONTRACT_ID }, -#endif -#ifdef MOZ_WIDGET_ANDROID - { PRESENTATION_DEVICE_PROVIDER_CATEGORY, "LegacyMDNSDeviceProvider", LEGACY_MDNS_PROVIDER_CONTRACT_ID }, -#endif //MOZ_WIDGET_ANDROID - { nullptr } -}; - -static const mozilla::Module kPresentationDeviceProviderModule = { - mozilla::Module::kVersion, - kPresentationDeviceProviderCIDs, - kPresentationDeviceProviderContracts, - kPresentationDeviceProviderCategories -}; - -NSMODULE_DEFN(PresentationDeviceProviderModule) = &kPresentationDeviceProviderModule; diff --git a/dom/presentation/provider/ReceiverStateMachine.jsm b/dom/presentation/provider/ReceiverStateMachine.jsm deleted file mode 100644 index 23ebb5a4e..000000000 --- a/dom/presentation/provider/ReceiverStateMachine.jsm +++ /dev/null @@ -1,238 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components, dump */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["ReceiverStateMachine"]; // jshint ignore:line - -const { utils: Cu } = Components; - -/* globals State, CommandType */ -Cu.import("resource://gre/modules/presentation/StateMachineHelper.jsm"); - -const DEBUG = false; -function debug(str) { - dump("-*- ReceiverStateMachine: " + str + "\n"); -} - -var handlers = [ - function _initHandler(stateMachine, command) { - // shouldn't receive any command at init state - DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line - }, - function _connectingHandler(stateMachine, command) { - switch (command.type) { - case CommandType.CONNECT: - stateMachine._sendCommand({ - type: CommandType.CONNECT_ACK - }); - stateMachine.state = State.CONNECTED; - stateMachine._notifyDeviceConnected(command.deviceId); - break; - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command - break; - } - }, - function _connectedHandler(stateMachine, command) { - switch (command.type) { - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - case CommandType.LAUNCH: - stateMachine._notifyLaunch(command.presentationId, - command.url); - stateMachine._sendCommand({ - type: CommandType.LAUNCH_ACK, - presentationId: command.presentationId - }); - break; - case CommandType.TERMINATE: - stateMachine._notifyTerminate(command.presentationId); - break; - case CommandType.TERMINATE_ACK: - stateMachine._notifyTerminate(command.presentationId); - break; - case CommandType.OFFER: - case CommandType.ICE_CANDIDATE: - stateMachine._notifyChannelDescriptor(command); - break; - case CommandType.RECONNECT: - stateMachine._notifyReconnect(command.presentationId, - command.url); - stateMachine._sendCommand({ - type: CommandType.RECONNECT_ACK, - presentationId: command.presentationId - }); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command - break; - } - }, - function _closingHandler(stateMachine, command) { - switch (command.type) { - case CommandType.DISCONNECT: - stateMachine.state = State.CLOSED; - stateMachine._notifyDisconnected(command.reason); - break; - default: - debug("unexpected command: " + JSON.stringify(command)); - // ignore unexpected command - break; - } - }, - function _closedHandler(stateMachine, command) { - // ignore every command in closed state. - DEBUG && debug("unexpected command: " + JSON.stringify(command)); // jshint ignore:line - }, -]; - -function ReceiverStateMachine(channel) { - this.state = State.INIT; - this._channel = channel; -} - -ReceiverStateMachine.prototype = { - launch: function _launch() { - // presentation session can only be launched by controlling UA. - debug("receiver shouldn't trigger launch"); - }, - - terminate: function _terminate(presentationId) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.TERMINATE, - presentationId: presentationId, - }); - } - }, - - terminateAck: function _terminateAck(presentationId) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.TERMINATE_ACK, - presentationId: presentationId, - }); - } - }, - - reconnect: function _reconnect() { - debug("receiver shouldn't trigger reconnect"); - }, - - sendOffer: function _sendOffer() { - // offer can only be sent by controlling UA. - debug("receiver shouldn't generate offer"); - }, - - sendAnswer: function _sendAnswer(answer) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.ANSWER, - answer: answer, - }); - } - }, - - updateIceCandidate: function _updateIceCandidate(candidate) { - if (this.state === State.CONNECTED) { - this._sendCommand({ - type: CommandType.ICE_CANDIDATE, - candidate: candidate, - }); - } - }, - - onCommand: function _onCommand(command) { - handlers[this.state](this, command); - }, - - onChannelReady: function _onChannelReady() { - if (this.state === State.INIT) { - this.state = State.CONNECTING; - } - }, - - onChannelClosed: function _onChannelClose(reason, isByRemote) { - switch (this.state) { - case State.CONNECTED: - if (isByRemote) { - this.state = State.CLOSED; - this._notifyDisconnected(reason); - } else { - this._sendCommand({ - type: CommandType.DISCONNECT, - reason: reason - }); - this.state = State.CLOSING; - this._closeReason = reason; - } - break; - case State.CLOSING: - if (isByRemote) { - this.state = State.CLOSED; - if (this._closeReason) { - reason = this._closeReason; - delete this._closeReason; - } - this._notifyDisconnected(reason); - } else { - // do nothing and wait for remote channel closed. - } - break; - default: - DEBUG && debug("unexpected channel close: " + reason + ", " + isByRemote); // jshint ignore:line - break; - } - }, - - _sendCommand: function _sendCommand(command) { - this._channel.sendCommand(command); - }, - - _notifyDeviceConnected: function _notifyDeviceConnected(deviceName) { - this._channel.notifyDeviceConnected(deviceName); - }, - - _notifyDisconnected: function _notifyDisconnected(reason) { - this._channel.notifyDisconnected(reason); - }, - - _notifyLaunch: function _notifyLaunch(presentationId, url) { - this._channel.notifyLaunch(presentationId, url); - }, - - _notifyTerminate: function _notifyTerminate(presentationId) { - this._channel.notifyTerminate(presentationId); - }, - - _notifyReconnect: function _notifyReconnect(presentationId, url) { - this._channel.notifyReconnect(presentationId, url); - }, - - _notifyChannelDescriptor: function _notifyChannelDescriptor(command) { - switch (command.type) { - case CommandType.OFFER: - this._channel.notifyOffer(command.offer); - break; - case CommandType.ICE_CANDIDATE: - this._channel.notifyIceCandidate(command.candidate); - break; - } - }, -}; - -this.ReceiverStateMachine = ReceiverStateMachine; // jshint ignore:line diff --git a/dom/presentation/provider/StateMachineHelper.jsm b/dom/presentation/provider/StateMachineHelper.jsm deleted file mode 100644 index 6e07863d4..000000000 --- a/dom/presentation/provider/StateMachineHelper.jsm +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["State", "CommandType"]; // jshint ignore:line - -const State = Object.freeze({ - INIT: 0, - CONNECTING: 1, - CONNECTED: 2, - CLOSING: 3, - CLOSED: 4, -}); - -const CommandType = Object.freeze({ - // control channel life cycle - CONNECT: "connect", // { deviceId: <string> } - CONNECT_ACK: "connect-ack", // { presentationId: <string> } - DISCONNECT: "disconnect", // { reason: <int> } - // presentation session life cycle - LAUNCH: "launch", // { presentationId: <string>, url: <string> } - LAUNCH_ACK: "launch-ack", // { presentationId: <string> } - TERMINATE: "terminate", // { presentationId: <string> } - TERMINATE_ACK: "terminate-ack", // { presentationId: <string> } - RECONNECT: "reconnect", // { presentationId: <string> } - RECONNECT_ACK: "reconnect-ack", // { presentationId: <string> } - // session transport establishment - OFFER: "offer", // { offer: <json> } - ANSWER: "answer", // { answer: <json> } - ICE_CANDIDATE: "ice-candidate", // { candidate: <string> } -}); - -this.State = State; // jshint ignore:line -this.CommandType = CommandType; // jshint ignore:line diff --git a/dom/presentation/provider/moz.build b/dom/presentation/provider/moz.build deleted file mode 100644 index 18428b50e..000000000 --- a/dom/presentation/provider/moz.build +++ /dev/null @@ -1,40 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXTRA_COMPONENTS += [ - 'BuiltinProviders.manifest', - 'PresentationControlService.js' -] - -UNIFIED_SOURCES += [ - 'DeviceProviderHelpers.cpp', - 'DisplayDeviceProvider.cpp', - 'MulticastDNSDeviceProvider.cpp', - 'PresentationDeviceProviderModule.cpp', -] - -EXTRA_JS_MODULES.presentation += [ - 'ControllerStateMachine.jsm', - 'ReceiverStateMachine.jsm', - 'StateMachineHelper.jsm', -] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - EXTRA_COMPONENTS += [ - # For android presentation device - 'AndroidCastDeviceProvider.js', - 'AndroidCastDeviceProvider.manifest', - # for TV 2.5 device backward capability - 'LegacyPresentationControlService.js', - 'LegacyProviders.manifest', - ] - - UNIFIED_SOURCES += [ - 'LegacyMDNSDeviceProvider.cpp', - ] - -include('/ipc/chromium/chromium-config.mozbuild') -FINAL_LIBRARY = 'xul' diff --git a/dom/presentation/provider/nsTCPDeviceInfo.h b/dom/presentation/provider/nsTCPDeviceInfo.h deleted file mode 100644 index 118f6c8ac..000000000 --- a/dom/presentation/provider/nsTCPDeviceInfo.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- 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 __TCPDeviceInfo_h__ -#define __TCPDeviceInfo_h__ - -namespace mozilla { -namespace dom { -namespace presentation { - -class TCPDeviceInfo final : public nsITCPDeviceInfo -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSITCPDEVICEINFO - - explicit TCPDeviceInfo(const nsACString& aId, - const nsACString& aAddress, - const uint16_t aPort, - const nsACString& aCertFingerprint) - : mId(aId) - , mAddress(aAddress) - , mPort(aPort) - , mCertFingerprint(aCertFingerprint) - { - } - -private: - virtual ~TCPDeviceInfo() {} - - nsCString mId; - nsCString mAddress; - uint16_t mPort; - nsCString mCertFingerprint; -}; - -NS_IMPL_ISUPPORTS(TCPDeviceInfo, - nsITCPDeviceInfo) - -// nsITCPDeviceInfo -NS_IMETHODIMP -TCPDeviceInfo::GetId(nsACString& aId) -{ - aId = mId; - return NS_OK; -} - -NS_IMETHODIMP -TCPDeviceInfo::GetAddress(nsACString& aAddress) -{ - aAddress = mAddress; - return NS_OK; -} - -NS_IMETHODIMP -TCPDeviceInfo::GetPort(uint16_t* aPort) -{ - *aPort = mPort; - return NS_OK; -} - -NS_IMETHODIMP -TCPDeviceInfo::GetCertFingerprint(nsACString& aCertFingerprint) -{ - aCertFingerprint = mCertFingerprint; - return NS_OK; -} - -} // namespace presentation -} // namespace dom -} // namespace mozilla - -#endif /* !__TCPDeviceInfo_h__ */ - diff --git a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js b/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js deleted file mode 100644 index 2bc069f6b..000000000 --- a/dom/presentation/tests/mochitest/PresentationDeviceInfoChromeScript.js +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -'use strict'; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm'); - -const { XPCOMUtils } = Cu.import('resource://gre/modules/XPCOMUtils.jsm'); - -const manager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - -var testProvider = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]), - forceDiscovery: function() { - sendAsyncMessage('force-discovery'); - }, - listener: null, -}; - -var testDevice = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - establishControlChannel: function() { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return true; - }, - id: null, - name: null, - type: null, - listener: null, -}; - -var testDevice1 = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'dummyid', - name: 'dummyName', - type: 'dummyType', - establishControlChannel: function(url, presentationId) { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return true; - }, -}; - -var testDevice2 = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'dummyid', - name: 'dummyName', - type: 'dummyType', - establishControlChannel: function(url, presentationId) { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return true; - }, -}; - -var mockedDeviceWithoutSupportedURL = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'dummyid', - name: 'dummyName', - type: 'dummyType', - establishControlChannel: function(url, presentationId) { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return false; - }, -}; - -var mockedDeviceSupportHttpsURL = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'dummyid', - name: 'dummyName', - type: 'dummyType', - establishControlChannel: function(url, presentationId) { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - if (requestedUrl.indexOf("https://") != -1) { - return true; - } - return false; - }, -}; - -addMessageListener('setup', function() { - manager.addDeviceProvider(testProvider); - - sendAsyncMessage('setup-complete'); -}); - -addMessageListener('trigger-device-add', function(device) { - testDevice.id = device.id; - testDevice.name = device.name; - testDevice.type = device.type; - manager.addDevice(testDevice); -}); - -addMessageListener('trigger-add-unsupport-url-device', function() { - manager.addDevice(mockedDeviceWithoutSupportedURL); -}); - -addMessageListener('trigger-add-multiple-devices', function() { - manager.addDevice(testDevice1); - manager.addDevice(testDevice2); -}); - -addMessageListener('trigger-add-https-devices', function() { - manager.addDevice(mockedDeviceSupportHttpsURL); -}); - - -addMessageListener('trigger-device-update', function(device) { - testDevice.id = device.id; - testDevice.name = device.name; - testDevice.type = device.type; - manager.updateDevice(testDevice); -}); - -addMessageListener('trigger-device-remove', function() { - manager.removeDevice(testDevice); -}); - -addMessageListener('trigger-remove-unsupported-device', function() { - manager.removeDevice(mockedDeviceWithoutSupportedURL); -}); - -addMessageListener('trigger-remove-multiple-devices', function() { - manager.removeDevice(testDevice1); - manager.removeDevice(testDevice2); -}); - -addMessageListener('trigger-remove-https-devices', function() { - manager.removeDevice(mockedDeviceSupportHttpsURL); -}); - -addMessageListener('teardown', function() { - manager.removeDeviceProvider(testProvider); -}); diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js deleted file mode 100644 index 3052bdcb1..000000000 --- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js +++ /dev/null @@ -1,470 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ -'use strict'; - -const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); -Cu.import('resource://gre/modules/Timer.jsm'); - -const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - -function registerMockedFactory(contractId, mockedClassId, mockedFactory) { - var originalClassId, originalFactory; - - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - if (!registrar.isCIDRegistered(mockedClassId)) { - try { - originalClassId = registrar.contractIDToCID(contractId); - originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); - } catch (ex) { - originalClassId = ""; - originalFactory = null; - } - if (originalFactory) { - registrar.unregisterFactory(originalClassId, originalFactory); - } - registrar.registerFactory(mockedClassId, "", contractId, mockedFactory); - } - - return { contractId: contractId, - mockedClassId: mockedClassId, - mockedFactory: mockedFactory, - originalClassId: originalClassId, - originalFactory: originalFactory }; -} - -function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { - if (originalFactory) { - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.unregisterFactory(mockedClassId, mockedFactory); - registrar.registerFactory(originalClassId, "", contractId, originalFactory); - } -} - -var sessionId = 'test-session-id-' + uuidGenerator.generateUUID().toString(); - -const address = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); -address.data = "127.0.0.1"; -const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); -addresses.appendElement(address, false); - -const mockedChannelDescription = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), - get type() { - if (Services.prefs.getBoolPref("dom.presentation.session_transport.data_channel.enable")) { - return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL; - } - return Ci.nsIPresentationChannelDescription.TYPE_TCP; - }, - tcpAddress: addresses, - tcpPort: 1234, -}; - -const mockedServerSocket = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIServerSocket, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - get port() { - return this._port; - }, - set listener(listener) { - this._listener = listener; - }, - init: function(port, loopbackOnly, backLog) { - if (port != -1) { - this._port = port; - } else { - this._port = 5678; - } - }, - asyncListen: function(listener) { - this._listener = listener; - }, - close: function() { - this._listener.onStopListening(this, Cr.NS_BINDING_ABORTED); - }, - simulateOnSocketAccepted: function(serverSocket, socketTransport) { - this._listener.onSocketAccepted(serverSocket, socketTransport); - } -}; - -const mockedSocketTransport = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISocketTransport]), -}; - -const mockedControlChannel = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - set listener(listener) { - this._listener = listener; - }, - get listener() { - return this._listener; - }, - sendOffer: function(offer) { - sendAsyncMessage('offer-sent', this._isValidSDP(offer)); - }, - sendAnswer: function(answer) { - sendAsyncMessage('answer-sent', this._isValidSDP(answer)); - - if (answer.type == Ci.nsIPresentationChannelDescription.TYPE_TCP) { - this._listener.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); - } - }, - _isValidSDP: function(aSDP) { - var isValid = false; - if (aSDP.type == Ci.nsIPresentationChannelDescription.TYPE_TCP) { - try { - var addresses = aSDP.tcpAddress; - if (addresses.length > 0) { - for (var i = 0; i < addresses.length; i++) { - // Ensure CString addresses are used. Otherwise, an error will be thrown. - addresses.queryElementAt(i, Ci.nsISupportsCString); - } - - isValid = true; - } - } catch (e) { - isValid = false; - } - } else if (aSDP.type == Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL) { - isValid = (aSDP.dataChannelSDP == "test-sdp"); - } - return isValid; - }, - launch: function(presentationId, url) { - sessionId = presentationId; - }, - terminate: function(presentationId) { - sendAsyncMessage('sender-terminate', presentationId); - }, - reconnect: function(presentationId, url) { - sendAsyncMessage('start-reconnect', url); - }, - notifyReconnected: function() { - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyReconnected(); - }, - disconnect: function(reason) { - sendAsyncMessage('control-channel-closed', reason); - this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyDisconnected(reason); - }, - simulateReceiverReady: function() { - this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyReceiverReady(); - }, - simulateOnOffer: function() { - sendAsyncMessage('offer-received'); - this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onOffer(mockedChannelDescription); - }, - simulateOnAnswer: function() { - sendAsyncMessage('answer-received'); - this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).onAnswer(mockedChannelDescription); - }, - simulateNotifyConnected: function() { - sendAsyncMessage('control-channel-opened'); - this._listener.QueryInterface(Ci.nsIPresentationControlChannelListener).notifyConnected(); - }, -}; - -const mockedDevice = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'id', - name: 'name', - type: 'type', - establishControlChannel: function(url, presentationId) { - sendAsyncMessage('control-channel-established'); - return mockedControlChannel; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return true; - }, -}; - -const mockedDevicePrompt = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - set request(request) { - this._request = request; - }, - get request() { - return this._request; - }, - promptDeviceSelection: function(request) { - this._request = request; - sendAsyncMessage('device-prompt'); - }, - simulateSelect: function() { - this._request.select(mockedDevice); - }, - simulateCancel: function(result) { - this._request.cancel(result); - } -}; - -const mockedSessionTransport = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport, - Ci.nsIPresentationSessionTransportBuilder, - Ci.nsIPresentationTCPSessionTransportBuilder, - Ci.nsIPresentationDataChannelSessionTransportBuilder, - Ci.nsIPresentationControlChannelListener, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - set callback(callback) { - this._callback = callback; - }, - get callback() { - return this._callback; - }, - get selfAddress() { - return this._selfAddress; - }, - buildTCPSenderTransport: function(transport, listener) { - this._listener = listener; - this._role = Ci.nsIPresentationService.ROLE_CONTROLLER; - this._listener.onSessionTransport(this); - this._listener = null; - sendAsyncMessage('data-transport-initialized'); - - setTimeout(()=>{ - this.simulateTransportReady(); - }, 0); - }, - buildTCPReceiverTransport: function(description, listener) { - this._listener = listener; - this._role = Ci.nsIPresentationService.ROLE_RECEIVER; - - var addresses = description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpAddress; - this._selfAddress = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsINetAddr]), - address: (addresses.length > 0) ? - addresses.queryElementAt(0, Ci.nsISupportsCString).data : "", - port: description.QueryInterface(Ci.nsIPresentationChannelDescription).tcpPort, - }; - - setTimeout(()=>{ - this._listener.onSessionTransport(this); - this._listener = null; - }, 0); - }, - // in-process case - buildDataChannelTransport: function(role, window, listener) { - this._listener = listener; - this._role = role; - - var hasNavigator = window ? (typeof window.navigator != "undefined") : false; - sendAsyncMessage('check-navigator', hasNavigator); - - setTimeout(()=>{ - this._listener.onSessionTransport(this); - this._listener = null; - this.simulateTransportReady(); - }, 0); - }, - enableDataNotification: function() { - sendAsyncMessage('data-transport-notification-enabled'); - }, - send: function(data) { - sendAsyncMessage('message-sent', data); - }, - close: function(reason) { - sendAsyncMessage('data-transport-closed', reason); - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportClosed(reason); - }, - simulateTransportReady: function() { - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); - }, - simulateIncomingMessage: function(message) { - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false); - }, - onOffer: function(aOffer) { - }, - onAnswer: function(aAnswer) { - } -}; - -const mockedNetworkInfo = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInfo]), - getAddresses: function(ips, prefixLengths) { - ips.value = ["127.0.0.1"]; - prefixLengths.value = [0]; - return 1; - }, -}; - -const mockedNetworkManager = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - get activeNetworkInfo() { - return mockedNetworkInfo; - }, -}; - -var requestPromise = null; - -const mockedRequestUIGlue = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - sendRequest: function(aUrl, aSessionId) { - sendAsyncMessage('receiver-launching', aSessionId); - return requestPromise; - }, -}; - -// Register mocked factories. -const originalFactoryData = []; -originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation-device/prompt;1", - uuidGenerator.generateUUID(), - mockedDevicePrompt)); -originalFactoryData.push(registerMockedFactory("@mozilla.org/network/server-socket;1", - uuidGenerator.generateUUID(), - mockedServerSocket)); -originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/presentationtcpsessiontransport;1", - uuidGenerator.generateUUID(), - mockedSessionTransport)); -originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/datachanneltransportbuilder;1", - uuidGenerator.generateUUID(), - mockedSessionTransport)); -originalFactoryData.push(registerMockedFactory("@mozilla.org/network/manager;1", - uuidGenerator.generateUUID(), - mockedNetworkManager)); -originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/requestuiglue;1", - uuidGenerator.generateUUID(), - mockedRequestUIGlue)); - -function tearDown() { - requestPromise = null; - mockedServerSocket.listener = null; - mockedControlChannel.listener = null; - mockedDevice.listener = null; - mockedDevicePrompt.request = null; - mockedSessionTransport.callback = null; - - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener).removeDevice(mockedDevice); - - // Register original factories. - for (var data of originalFactoryData) { - registerOriginalFactory(data.contractId, data.mockedClassId, - data.mockedFactory, data.originalClassId, - data.originalFactory); - } - - sendAsyncMessage('teardown-complete'); -} - -addMessageListener('trigger-device-add', function() { - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener).addDevice(mockedDevice); -}); - -addMessageListener('trigger-device-prompt-select', function() { - mockedDevicePrompt.simulateSelect(); -}); - -addMessageListener('trigger-device-prompt-cancel', function(result) { - mockedDevicePrompt.simulateCancel(result); -}); - -addMessageListener('trigger-incoming-session-request', function(url) { - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onSessionRequest(mockedDevice, url, sessionId, mockedControlChannel); -}); - -addMessageListener('trigger-incoming-terminate-request', function() { - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onTerminateRequest(mockedDevice, sessionId, mockedControlChannel, true); -}); - -addMessageListener('trigger-reconnected-acked', function(url) { - mockedControlChannel.notifyReconnected(); -}); - -addMessageListener('trigger-incoming-offer', function() { - mockedControlChannel.simulateOnOffer(); -}); - -addMessageListener('trigger-incoming-answer', function() { - mockedControlChannel.simulateOnAnswer(); -}); - -addMessageListener('trigger-incoming-transport', function() { - mockedServerSocket.simulateOnSocketAccepted(mockedServerSocket, mockedSocketTransport); -}); - -addMessageListener('trigger-control-channel-open', function(reason) { - mockedControlChannel.simulateNotifyConnected(); -}); - -addMessageListener('trigger-control-channel-close', function(reason) { - mockedControlChannel.disconnect(reason); -}); - -addMessageListener('trigger-data-transport-close', function(reason) { - mockedSessionTransport.close(reason); -}); - -addMessageListener('trigger-incoming-message', function(message) { - mockedSessionTransport.simulateIncomingMessage(message); -}); - -addMessageListener('teardown', function() { - tearDown(); -}); - -var controlChannelListener; -addMessageListener('save-control-channel-listener', function() { - controlChannelListener = mockedControlChannel.listener; -}); - -addMessageListener('restore-control-channel-listener', function(message) { - mockedControlChannel.listener = controlChannelListener; - controlChannelListener = null; -}); - -var obs = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); -obs.addObserver(function observer(aSubject, aTopic, aData) { - obs.removeObserver(observer, aTopic); - - requestPromise = aSubject; -}, 'setup-request-promise', false); diff --git a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js b/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js deleted file mode 100644 index 82d7362b2..000000000 --- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript1UA.js +++ /dev/null @@ -1,366 +0,0 @@ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* 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/. */ - -'use strict'; - -const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - -function debug(str) { - // dump('DEBUG -*- PresentationSessionChromeScript1UA -*-: ' + str + '\n'); -} - -const originalFactoryData = []; -var sessionId; // Store the uuid generated by PresentationRequest. -var triggerControlChannelError = false; // For simulating error during control channel establishment. - -// control channel of sender -const mockControlChannelOfSender = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - set listener(listener) { - // PresentationControllingInfo::SetControlChannel - if (listener) { - debug('set listener for mockControlChannelOfSender without null'); - } else { - debug('set listener for mockControlChannelOfSender with null'); - } - this._listener = listener; - }, - get listener() { - return this._listener; - }, - notifyConnected: function() { - // send offer after notifyConnected immediately - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyConnected(); - }, - notifyReconnected: function() { - // send offer after notifyOpened immediately - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyReconnected(); - }, - sendOffer: function(offer) { - Services.tm.mainThread.dispatch(() => { - mockControlChannelOfReceiver.onOffer(offer); - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - onAnswer: function(answer) { - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .onAnswer(answer); - }, - launch: function(presentationId, url) { - sessionId = presentationId; - sendAsyncMessage('sender-launch', url); - }, - disconnect: function(reason) { - if (!this._listener) { - return; - } - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyDisconnected(reason); - mockControlChannelOfReceiver.disconnect(); - }, - terminate: function(presentationId) { - sendAsyncMessage('sender-terminate'); - }, - reconnect: function(presentationId, url) { - sendAsyncMessage('start-reconnect', url); - }, - sendIceCandidate: function(candidate) { - mockControlChannelOfReceiver.notifyIceCandidate(candidate); - }, - notifyIceCandidate: function(candidate) { - if (!this._listener) { - return; - } - - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .onIceCandidate(candidate); - }, -}; - -// control channel of receiver -const mockControlChannelOfReceiver = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - set listener(listener) { - // PresentationPresentingInfo::SetControlChannel - if (listener) { - debug('set listener for mockControlChannelOfReceiver without null'); - } else { - debug('set listener for mockControlChannelOfReceiver with null'); - } - this._listener = listener; - - if (this._pendingOpened) { - this._pendingOpened = false; - this.notifyConnected(); - } - }, - get listener() { - return this._listener; - }, - notifyConnected: function() { - // do nothing - if (!this._listener) { - this._pendingOpened = true; - return; - } - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyConnected(); - }, - onOffer: function(offer) { - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .onOffer(offer); - }, - sendAnswer: function(answer) { - Services.tm.mainThread.dispatch(() => { - mockControlChannelOfSender.onAnswer(answer); - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - disconnect: function(reason) { - if (!this._listener) { - return; - } - - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .notifyDisconnected(reason); - sendAsyncMessage('control-channel-receiver-closed', reason); - }, - terminate: function(presentaionId) { - }, - sendIceCandidate: function(candidate) { - mockControlChannelOfReceiver.notifyIceCandidate(candidate); - }, - notifyIceCandidate: function(candidate) { - if (!this._listener) { - return; - } - - this._listener - .QueryInterface(Ci.nsIPresentationControlChannelListener) - .onIceCandidate(candidate); - }, -}; - -const mockDevice = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'id', - name: 'name', - type: 'type', - establishControlChannel: function(url, presentationId) { - if (triggerControlChannelError) { - throw Cr.NS_ERROR_FAILURE; - } - sendAsyncMessage('control-channel-established'); - return mockControlChannelOfSender; - }, - disconnect: function() { - sendAsyncMessage('device-disconnected'); - }, - isRequestedUrlSupported: function(requestedUrl) { - return true; - }, -}; - -const mockDevicePrompt = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt, - Ci.nsIFactory]), - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - set request(request) { - this._request = request; - }, - get request() { - return this._request; - }, - promptDeviceSelection: function(request) { - this._request = request; - sendAsyncMessage('device-prompt'); - }, - simulateSelect: function() { - this._request.select(mockDevice); - }, - simulateCancel: function() { - this._request.cancel(); - } -}; - -const mockRequestUIGlue = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue, - Ci.nsIFactory]), - set promise(aPromise) { - this._promise = aPromise - }, - get promise() { - return this._promise; - }, - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - sendRequest: function(aUrl, aSessionId) { - return this.promise; - }, -}; - -function initMockAndListener() { - - function registerMockFactory(contractId, mockClassId, mockFactory) { - var originalClassId, originalFactory; - - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - if (!registrar.isCIDRegistered(mockClassId)) { - try { - originalClassId = registrar.contractIDToCID(contractId); - originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); - } catch (ex) { - originalClassId = ""; - originalFactory = null; - } - if (originalFactory) { - registrar.unregisterFactory(originalClassId, originalFactory); - } - registrar.registerFactory(mockClassId, "", contractId, mockFactory); - } - - return { contractId: contractId, - mockClassId: mockClassId, - mockFactory: mockFactory, - originalClassId: originalClassId, - originalFactory: originalFactory }; - } - // Register mock factories. - const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - originalFactoryData.push(registerMockFactory("@mozilla.org/presentation-device/prompt;1", - uuidGenerator.generateUUID(), - mockDevicePrompt)); - originalFactoryData.push(registerMockFactory("@mozilla.org/presentation/requestuiglue;1", - uuidGenerator.generateUUID(), - mockRequestUIGlue)); - - addMessageListener('trigger-device-add', function() { - debug('Got message: trigger-device-add'); - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .addDevice(mockDevice); - }); - - addMessageListener('trigger-device-prompt-select', function() { - debug('Got message: trigger-device-prompt-select'); - mockDevicePrompt.simulateSelect(); - }); - - addMessageListener('trigger-on-session-request', function(url) { - debug('Got message: trigger-on-session-request'); - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onSessionRequest(mockDevice, - url, - sessionId, - mockControlChannelOfReceiver); - }); - - addMessageListener('trigger-on-terminate-request', function() { - debug('Got message: trigger-on-terminate-request'); - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onTerminateRequest(mockDevice, - sessionId, - mockControlChannelOfReceiver, - false); - }); - - addMessageListener('trigger-control-channel-open', function(reason) { - debug('Got message: trigger-control-channel-open'); - mockControlChannelOfSender.notifyConnected(); - mockControlChannelOfReceiver.notifyConnected(); - }); - - addMessageListener('trigger-control-channel-error', function(reason) { - debug('Got message: trigger-control-channel-open'); - triggerControlChannelError = true; - }); - - addMessageListener('trigger-reconnected-acked', function(url) { - debug('Got message: trigger-reconnected-acked'); - mockControlChannelOfSender.notifyReconnected(); - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onReconnectRequest(mockDevice, - url, - sessionId, - mockControlChannelOfReceiver); - }); - - // Used to call sendAsyncMessage in chrome script from receiver. - addMessageListener('forward-command', function(command_data) { - let command = JSON.parse(command_data); - sendAsyncMessage(command.name, command.data); - }); - - addMessageListener('teardown', teardown); - - var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); - obs.addObserver(function setupRequestPromiseHandler(aSubject, aTopic, aData) { - debug('Got observer: setup-request-promise'); - obs.removeObserver(setupRequestPromiseHandler, aTopic); - mockRequestUIGlue.promise = aSubject; - sendAsyncMessage('promise-setup-ready'); - }, 'setup-request-promise', false); -} - -function teardown() { - - function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { - if (originalFactory) { - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.unregisterFactory(mockedClassId, mockedFactory); - registrar.registerFactory(originalClassId, "", contractId, originalFactory); - } - } - - mockRequestUIGlue.promise = null; - mockControlChannelOfSender.listener = null; - mockControlChannelOfReceiver.listener = null; - mockDevicePrompt.request = null; - - var deviceManager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener) - .removeDevice(mockDevice); - // Register original factories. - for (var data of originalFactoryData) { - registerOriginalFactory(data.contractId, data.mockClassId, - data.mockFactory, data.originalClassId, - data.originalFactory); - } - sendAsyncMessage('teardown-complete'); -} - -initMockAndListener(); diff --git a/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js b/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js deleted file mode 100644 index 77240ab5f..000000000 --- a/dom/presentation/tests/mochitest/PresentationSessionFrameScript.js +++ /dev/null @@ -1,258 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -function loadPrivilegedScriptTest() { - /** - * The script is loaded as - * (a) a privileged script in content process for dc_sender.html - * (b) a frame script in the remote iframe process for dc_receiver_oop.html - * |type port == "undefined"| indicates the script is load by - * |loadPrivilegedScript| which is the first case. - */ - function sendMessage(type, data) { - if (typeof port == "undefined") { - sendAsyncMessage(type, {'data': data}); - } else { - port.postMessage({'type': type, - 'data': data - }); - } - } - - if (typeof port != "undefined") { - /** - * When the script is loaded by |loadPrivilegedScript|, these APIs - * are exposed to this script. - */ - port.onmessage = (e) => { - var type = e.data['type']; - if (!handlers.hasOwnProperty(type)) { - return; - } - var args = [e]; - handlers[type].forEach(handler => handler.apply(null, args)); - }; - var handlers = {}; - addMessageListener = function(message, handler) { - if (handlers.hasOwnProperty(message)) { - handlers[message].push(handler); - } else { - handlers[message] = [handler]; - } - }; - removeMessageListener = function(message, handler) { - if (!handler || !handlers.hasOwnProperty(message)) { - return; - } - var index = handlers[message].indexOf(handler); - if (index != -1) { - handlers[message].splice(index, 1); - } - }; - } - - const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu, results: Cr } = Components; - - const mockedChannelDescription = { - QueryInterface : function (iid) { - const interfaces = [Ci.nsIPresentationChannelDescription]; - - if (!interfaces.some(v => iid.equals(v))) { - throw Cr.NS_ERROR_NO_INTERFACE; - } - return this; - }, - get type() { - if (Services.prefs.getBoolPref("dom.presentation.session_transport.data_channel.enable")) { - return Ci.nsIPresentationChannelDescription.TYPE_DATACHANNEL; - } - return Ci.nsIPresentationChannelDescription.TYPE_TCP; - }, - get dataChannelSDP() { - return "test-sdp"; - } - }; - - function setTimeout(callback, delay) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback({ notify: callback }, - delay, - Ci.nsITimer.TYPE_ONE_SHOT); - return timer; - } - - const mockedSessionTransport = { - QueryInterface : function (iid) { - const interfaces = [Ci.nsIPresentationSessionTransport, - Ci.nsIPresentationDataChannelSessionTransportBuilder, - Ci.nsIFactory]; - - if (!interfaces.some(v => iid.equals(v))) { - throw Cr.NS_ERROR_NO_INTERFACE; - } - return this; - }, - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return this.QueryInterface(aIID); - }, - set callback(callback) { - this._callback = callback; - }, - get callback() { - return this._callback; - }, - /* OOP case */ - buildDataChannelTransport: function(role, window, listener) { - dump("PresentationSessionFrameScript: build data channel transport\n"); - this._listener = listener; - this._role = role; - - var hasNavigator = window ? (typeof window.navigator != "undefined") : false; - sendMessage('check-navigator', hasNavigator); - - if (this._role == Ci.nsIPresentationService.ROLE_CONTROLLER) { - this._listener.sendOffer(mockedChannelDescription); - } - }, - - enableDataNotification: function() { - sendMessage('data-transport-notification-enabled'); - }, - send: function(data) { - sendMessage('message-sent', data); - }, - close: function(reason) { - sendMessage('data-transport-closed', reason); - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportClosed(reason); - this._callback = null; - }, - simulateTransportReady: function() { - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyTransportReady(); - }, - simulateIncomingMessage: function(message) { - this._callback.QueryInterface(Ci.nsIPresentationSessionTransportCallback).notifyData(message, false); - }, - onOffer: function(aOffer) { - this._listener.sendAnswer(mockedChannelDescription); - this._onSessionTransport(); - }, - onAnswer: function(aAnswer) { - this._onSessionTransport(); - }, - _onSessionTransport: function() { - setTimeout(()=>{ - this._listener.onSessionTransport(this); - this.simulateTransportReady(); - this._listener = null; - }, 0); - } - }; - - - function tearDown() { - mockedSessionTransport.callback = null; - - /* Register original factories. */ - for (var data of originalFactoryData) { - registerOriginalFactory(data.contractId, data.mockedClassId, - data.mockedFactory, data.originalClassId, - data.originalFactory); - } - sendMessage("teardown-complete"); - } - - - function registerMockedFactory(contractId, mockedClassId, mockedFactory) { - var originalClassId, originalFactory; - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - - if (!registrar.isCIDRegistered(mockedClassId)) { - try { - originalClassId = registrar.contractIDToCID(contractId); - originalFactory = Cm.getClassObject(Cc[contractId], Ci.nsIFactory); - } catch (ex) { - originalClassId = ""; - originalFactory = null; - } - if (originalFactory) { - registrar.unregisterFactory(originalClassId, originalFactory); - } - registrar.registerFactory(mockedClassId, "", contractId, mockedFactory); - } - - return { contractId: contractId, - mockedClassId: mockedClassId, - mockedFactory: mockedFactory, - originalClassId: originalClassId, - originalFactory: originalFactory }; - } - - function registerOriginalFactory(contractId, mockedClassId, mockedFactory, originalClassId, originalFactory) { - if (originalFactory) { - var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.unregisterFactory(mockedClassId, mockedFactory); - registrar.registerFactory(originalClassId, "", contractId, originalFactory); - } - } - - /* Register mocked factories. */ - const originalFactoryData = []; - const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - originalFactoryData.push(registerMockedFactory("@mozilla.org/presentation/datachanneltransportbuilder;1", - uuidGenerator.generateUUID(), - mockedSessionTransport)); - - addMessageListener('trigger-incoming-message', function(event) { - mockedSessionTransport.simulateIncomingMessage(event.data.data); - }); - addMessageListener('teardown', ()=>tearDown()); -} - -// Exposed to the caller of |loadPrivilegedScript| -var contentScript = { - handlers: {}, - addMessageListener: function(message, handler) { - if (this.handlers.hasOwnProperty(message)) { - this.handlers[message].push(handler); - } else { - this.handlers[message] = [handler]; - } - }, - removeMessageListener: function(message, handler) { - if (!handler || !this.handlers.hasOwnProperty(message)) { - return; - } - var index = this.handlers[message].indexOf(handler); - if (index != -1) { - this.handlers[message].splice(index, 1); - } - }, - sendAsyncMessage: function(message, data) { - port.postMessage({'type': message, - 'data': data - }); - } -} - -if (!SpecialPowers.isMainProcess()) { - var port; - try { - port = SpecialPowers.loadPrivilegedScript(loadPrivilegedScriptTest.toSource()); - } catch (e) { - ok(false, "loadPrivilegedScript shoulde not throw" + e); - } - - port.onmessage = (e) => { - var type = e.data['type']; - if (!contentScript.handlers.hasOwnProperty(type)) { - return; - } - var args = [e.data['data']]; - contentScript.handlers[type].forEach(handler => handler.apply(null, args)); - }; -} diff --git a/dom/presentation/tests/mochitest/chrome.ini b/dom/presentation/tests/mochitest/chrome.ini deleted file mode 100644 index 83841f4f8..000000000 --- a/dom/presentation/tests/mochitest/chrome.ini +++ /dev/null @@ -1,14 +0,0 @@ -[DEFAULT] -support-files = - PresentationDeviceInfoChromeScript.js - PresentationSessionChromeScript.js - -[test_presentation_datachannel_sessiontransport.html] -skip-if = os == 'android' -[test_presentation_device_info.html] -[test_presentation_sender_startWithDevice.html] -skip-if = toolkit == 'android' # Bug 1129785 -[test_presentation_tcp_sender.html] -skip-if = toolkit == 'android' # Bug 1129785 -[test_presentation_tcp_sender_default_request.html] -skip-if = toolkit == 'android' # Bug 1129785 diff --git a/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html b/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html deleted file mode 100644 index cf02d2b2c..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_1ua_receiver.html +++ /dev/null @@ -1,220 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver at receiver side</title> - </head> - <body> - <div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - if (a === b) { - alert('OK ' + msg); - } else { - alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); - } -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(name, data) { - alert('COMMAND ' + JSON.stringify({name: name, data: data})); -} - -function finish() { - alert('DONE'); -} - -var connection; -const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0]; -const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length); -const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER); -TYPED_DATA_ARRAY.set(DATA_ARRAY); - -function is_same_buffer(recv_data, expect_data) { - let recv_dataview = new Uint8Array(recv_data); - let expected_dataview = new Uint8Array(expect_data); - - if (recv_dataview.length !== expected_dataview.length) { - return false; - } - - for (let i = 0; i < recv_dataview.length; i++) { - if (recv_dataview[i] != expected_dataview[i]) { - info('discover byte differenct at ' + i); - return false; - } - } - return true; -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionAvailable ---'); - ok(navigator.presentation, "Receiver: navigator.presentation should be available."); - ok(navigator.presentation.receiver, "Receiver: navigator.presentation.receiver should be available."); - is(navigator.presentation.defaultRequest, null, "Receiver: navigator.presentation.defaultRequest should be null."); - - navigator.presentation.receiver.connectionList - .then((aList) => { - is(aList.connections.length, 1, "Should get one conncetion."); - connection = aList.connections[0]; - ok(connection.id, "Connection ID should be set: " + connection.id); - is(connection.state, "connected", "Connection state at receiver side should be connected."); - aResolve(); - }) - .catch((aError) => { - ok(false, "Receiver: Error occurred when getting the connection: " + aError); - finish(); - aReject(); - }); - }); -} - -function testConnectionReady() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionReady ---'); - connection.onconnect = function() { - connection.onconnect = null; - ok(false, "Should not get |onconnect| event.") - aReject(); - }; - if (connection.state === "connected") { - connection.onconnect = null; - is(connection.state, "connected", "Receiver: Connection state should become connected."); - aResolve(); - } - }); -} - -function testIncomingMessage() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testIncomingMessage ---'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let msg = evt.data; - is(msg, 'msg-sender-to-receiver', 'Receiver: Receiver should receive message from sender.'); - command('forward-command', JSON.stringify({ name: 'message-from-sender-received' })); - aResolve(); - }); - command('forward-command', JSON.stringify({ name: 'trigger-message-from-sender' })); - }); -} - -function testSendMessage() { - return new Promise(function(aResolve, aReject) { - window.addEventListener('hashchange', function hashchangeHandler(evt) { - var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); - if (message.type === 'trigger-message-from-receiver') { - info('Receiver: --- testSendMessage ---'); - connection.send('msg-receiver-to-sender'); - } - if (message.type === 'message-from-receiver-received') { - window.removeEventListener('hashchange', hashchangeHandler); - aResolve(); - } - }); - }); -} - -function testIncomingBlobMessage() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testIncomingBlobMessage ---'); - connection.send('testIncomingBlobMessage'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let recvData= String.fromCharCode.apply(null, new Uint8Array(evt.data)); - is(recvData, "Hello World", 'expected same string data'); - aResolve(); - }); - }); -} - -function testConnectionClosed() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionClosed ---'); - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Receiver: Connection should be closed."); - command('forward-command', JSON.stringify({ name: 'receiver-closed' })); - aResolve(); - }; - command('forward-command', JSON.stringify({ name: 'ready-to-close' })); - }); -} - -function testReconnectConnection() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testReconnectConnection ---'); - window.addEventListener('hashchange', function hashchangeHandler(evt) { - var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); - if (message.type === 'prepare-for-reconnect') { - command('forward-command', JSON.stringify({ name: 'ready-to-reconnect' })); - } - }); - connection.onconnect = function() { - connection.onconnect = null; - ok(true, "The connection is reconnected.") - aResolve(); - }; - }); -} - -function testIncomingArrayBuffer() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testIncomingArrayBuffer ---'); - connection.binaryType = "blob"; - connection.send('testIncomingArrayBuffer'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - var fileReader = new FileReader(); - fileReader.onload = function() { - ok(is_same_buffer(DATA_ARRAY_BUFFER, this.result), "expected same buffer data"); - aResolve(); - }; - fileReader.readAsArrayBuffer(evt.data); - }); - }); -} - -function testIncomingArrayBufferView() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testIncomingArrayBufferView ---'); - connection.binaryType = "arraybuffer"; - connection.send('testIncomingArrayBufferView'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - ok(is_same_buffer(evt.data, TYPED_DATA_ARRAY), "expected same buffer data"); - aResolve(); - }); - }); -} - -function runTests() { - testConnectionAvailable() - .then(testConnectionReady) - .then(testIncomingMessage) - .then(testSendMessage) - .then(testIncomingBlobMessage) - .then(testConnectionClosed) - .then(testReconnectConnection) - .then(testIncomingArrayBuffer) - .then(testIncomingArrayBufferView) - .then(testConnectionClosed); -} - -runTests(); - -</script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html b/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html deleted file mode 100644 index 370cb92e1..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_1ua_wentaway.html +++ /dev/null @@ -1,95 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver at receiver side</title> - </head> - <body> - <div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - if (a === b) { - alert('OK ' + msg); - } else { - alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); - } -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(name, data) { - alert('COMMAND ' + JSON.stringify({name: name, data: data})); -} - -function finish() { - alert('DONE'); -} - -var connection; - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionAvailable ---'); - ok(navigator.presentation, "Receiver: navigator.presentation should be available."); - ok(navigator.presentation.receiver, "Receiver: navigator.presentation.receiver should be available."); - - navigator.presentation.receiver.connectionList - .then((aList) => { - is(aList.connections.length, 1, "Should get one conncetion."); - connection = aList.connections[0]; - ok(connection.id, "Connection ID should be set: " + connection.id); - is(connection.state, "connected", "Connection state at receiver side should be connected."); - aResolve(); - }) - .catch((aError) => { - ok(false, "Receiver: Error occurred when getting the connection: " + aError); - finish(); - aReject(); - }); - }); -} - -function testConnectionReady() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionReady ---'); - connection.onconnect = function() { - connection.onconnect = null; - ok(false, "Should not get |onconnect| event.") - aReject(); - }; - if (connection.state === "connected") { - connection.onconnect = null; - is(connection.state, "connected", "Receiver: Connection state should become connected."); - aResolve(); - } - }); -} - -function testConnectionWentaway() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionWentaway ---\n'); - command('forward-command', JSON.stringify({ name: 'ready-to-remove-receiverFrame' })); - }); -} - -function runTests() { - testConnectionAvailable() - .then(testConnectionReady) - .then(testConnectionWentaway); -} - -runTests(); - -</script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html b/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html deleted file mode 100644 index f042d2994..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html +++ /dev/null @@ -1,159 +0,0 @@ - -<!DOCTYPE HTML> -<html> -<head> -<meta charset="utf-8"> -<title>Test allow-presentation sandboxing flag</title> -<script type="application/javascript;version=1.8"> - -"use strict"; - -function is(a, b, msg) { - window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); -} - -function ok(a, msg) { - window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); -} - -function info(msg) { - window.parent.postMessage("INFO " + msg, "*"); -} - -function command(msg) { - window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); -} - -function finish() { - window.parent.postMessage("DONE", "*"); -} - -function testGetAvailability() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available."); - var request = new PresentationRequest("http://example.com"); - - request.getAvailability().then( - function(aAvailability) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function testStartRequest() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("http://example.com"); - - request.start().then( - function(aAvailability) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function testReconnectRequest() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("http://example.com"); - - request.reconnect("dummyId").then( - function(aConnection) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function testGetAvailabilityForAboutBlank() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("about:blank"); - - request.getAvailability().then( - function(aAvailability) { - ok(true, "Success due to a priori authenticated URL."); - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - aReject(); - } - ); - }); -} - -function testGetAvailabilityForAboutSrcdoc() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("about:srcdoc"); - - request.getAvailability().then( - function(aAvailability) { - ok(true, "Success due to a priori authenticated URL."); - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - aReject(); - } - ); - }); -} - -function testGetAvailabilityForDataURL() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("data:text/html,1"); - - request.getAvailability().then( - function(aAvailability) { - ok(true, "Success due to a priori authenticated URL."); - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - aReject(); - } - ); - }); -} - -function runTest() { - testGetAvailability() - .then(testStartRequest) - .then(testReconnectRequest) - .then(testGetAvailabilityForAboutBlank) - .then(testGetAvailabilityForAboutSrcdoc) - .then(testGetAvailabilityForDataURL) - .then(finish); -} - -window.addEventListener("message", function onMessage(evt) { - window.removeEventListener("message", onMessage); - if (evt.data === "start") { - runTest(); - } -}, false); - -window.setTimeout(function() { - command("ready-to-start"); -}, 3000); - -</script> -</head> -<body> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_non_receiver.html b/dom/presentation/tests/mochitest/file_presentation_non_receiver.html deleted file mode 100644 index 1203523ac..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_non_receiver.html +++ /dev/null @@ -1,41 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver on a non-receiver page at receiver side</title> -</head> -<body> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - alert((a === b ? 'OK ' : 'KO ') + msg); -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function finish() { - alert('DONE'); -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in non-receiving pages."); - aResolve(); - }); -} - -testConnectionAvailable(). -then(finish); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html b/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html deleted file mode 100644 index c95eddf57..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver on a non-receiver inner iframe of the receiver page at receiver side</title> -</head> -<body onload="testConnectionAvailable()"> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - is(navigator.presentation.receiver, null, "navigator.presentation.receiver shouldn't be available in inner iframes with different origins from receiving pages."); - aResolve(); - }); -} - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver.html b/dom/presentation/tests/mochitest/file_presentation_receiver.html deleted file mode 100644 index 46a330b5f..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_receiver.html +++ /dev/null @@ -1,140 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver at receiver side</title> -</head> -<body> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - alert((a === b ? 'OK ' : 'KO ') + msg); -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(msg) { - alert('COMMAND ' + JSON.stringify(msg)); -} - -function finish() { - alert('DONE'); -} - -var connection; - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available in receiving pages."); - ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in receiving pages."); - - navigator.presentation.receiver.connectionList.then( - function(aList) { - is(aList.connections.length, 1, "Should get one conncetion."); - connection = aList.connections[0]; - ok(connection.id, "Connection ID should be set: " + connection.id); - is(connection.state, "connected", "Connection state at receiver side should be connected."); - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when getting the connection list: " + aError); - finish(); - aReject(); - } - ); - command({ name: 'trigger-incoming-offer' }); - }); -} - -function testDefaultRequestIsUndefined() { - return new Promise(function(aResolve, aReject) { - is(navigator.presentation.defaultRequest, undefined, "navigator.presentation.defaultRequest should not be available in receiving UA"); - aResolve(); - }); -} - -function testConnectionAvailableSameOriginInnerIframe() { - return new Promise(function(aResolve, aReject) { - var iframe = document.createElement('iframe'); - iframe.setAttribute('src', './file_presentation_receiver_inner_iframe.html'); - document.body.appendChild(iframe); - - aResolve(); - }); -} - -function testConnectionUnavailableDiffOriginInnerIframe() { - return new Promise(function(aResolve, aReject) { - var iframe = document.createElement('iframe'); - iframe.setAttribute('src', 'http://example.com/tests/dom/presentation/tests/mochitest/file_presentation_non_receiver_inner_iframe.html'); - document.body.appendChild(iframe); - - aResolve(); - }); -} - -function testConnectionListSameObject() { - return new Promise(function(aResolve, aReject) { - is(navigator.presentation.receiver.connectionList, navigator.presentation.receiver.connectionList, "The promise should be the same object."); - var promise = navigator.presentation.receiver.connectionList.then( - function(aList) { - is(connection, aList.connections[0], "The connection from list and the one from |connectionavailable| event should be the same."); - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when getting the connection list: " + aError); - finish(); - aReject(); - } - ); - }); -} - -function testIncomingMessage() { - return new Promise(function(aResolve, aReject) { - const incomingMessage = "test incoming message"; - - connection.addEventListener('message', function messageHandler(aEvent) { - connection.removeEventListener('message', messageHandler); - is(aEvent.data, incomingMessage, "An incoming message should be received."); - aResolve(); - }); - - command({ name: 'trigger-incoming-message', - data: incomingMessage }); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -testConnectionAvailable(). -then(testDefaultRequestIsUndefined). -then(testConnectionAvailableSameOriginInnerIframe). -then(testConnectionUnavailableDiffOriginInnerIframe). -then(testConnectionListSameObject). -then(testIncomingMessage). -then(testCloseConnection). -then(finish); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html b/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html deleted file mode 100644 index 3a6060310..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_receiver_auxiliary_navigation.html +++ /dev/null @@ -1,60 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for sandboxed auxiliary navigation flag in receiver page</title> -</head> -<body> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - alert((a === b ? 'OK ' : 'KO ') + msg); -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(msg) { - alert('COMMAND ' + JSON.stringify(msg)); -} - -function finish() { - alert('DONE'); -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available in OOP receiving pages."); - ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in receiving pages."); - - aResolve(); - }); -} - -function testOpenWindow() { - return new Promise(function(aResolve, aReject) { - try { - window.open("http://example.com"); - ok(false, "receiver page should not be able to open a new window."); - } catch(e) { - ok(true, "receiver page should not be able to open a new window."); - aResolve(); - } - }); -} - -testConnectionAvailable(). -then(testOpenWindow). -then(finish); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html b/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html deleted file mode 100644 index 6b1f2152f..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_receiver_establish_connection_error.html +++ /dev/null @@ -1,79 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for connection establishing errors of B2G Presentation API at receiver side</title> -</head> -<body> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function is(a, b, msg) { - if (a === b) { - alert('OK ' + msg); - } else { - alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); - } -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(name, data) { - alert('COMMAND ' + JSON.stringify({name: name, data: data})); -} - -function finish() { - alert('DONE'); -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available."); - ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available."); - aResolve(); - }); -} - -function testUnexpectedControlChannelClose() { - // Trigger the control channel to be closed with error code. - command({ name: 'trigger-control-channel-close', data: 0x80004004 /* NS_ERROR_ABORT */ }); - - return new Promise(function(aResolve, aReject) { - return Promise.race([ - navigator.presentation.receiver.connectionList.then( - (aList) => { - ok(false, "Should not get a connection list.") - aReject(); - }, - (aError) => { - ok(false, "Error occurred when getting the connection list: " + aError); - aReject(); - } - ), - new Promise( - () => { - setTimeout(() => { - ok(true, "Not getting a conenction list."); - aResolve(); - }, 3000); - } - ), - ]); - }); -} - -testConnectionAvailable(). -then(testUnexpectedControlChannelClose). -then(finish); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html b/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html deleted file mode 100644 index 3bd5ac4b1..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_receiver_inner_iframe.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE HTML> -<html> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationReceiver in an inner iframe of the receiver page at receiver side</title> -</head> -<body onload="testConnectionAvailable()"> -<div id="content"></div> -<script type="application/javascript;version=1.7"> - -"use strict"; - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation.receiver, "navigator.presentation.receiver should be available in same-origin inner iframes of receiving pages."); - aResolve(); - }); -} - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_reconnect.html b/dom/presentation/tests/mochitest/file_presentation_reconnect.html deleted file mode 100644 index 174ccd3f3..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_reconnect.html +++ /dev/null @@ -1,102 +0,0 @@ - -<!DOCTYPE HTML> -<html> -<head> -<meta charset="utf-8"> -<title>Test allow-presentation sandboxing flag</title> -<script type="application/javascript;version=1.8"> - -"use strict"; - -function is(a, b, msg) { - window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); -} - -function ok(a, msg) { - window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); -} - -function info(msg) { - window.parent.postMessage("INFO " + msg, "*"); -} - -function command(msg) { - window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); -} - -function finish() { - window.parent.postMessage("DONE", "*"); -} - -var request; -var connection; - -function testStartRequest() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available."); - request = new PresentationRequest("http://example1.com"); - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - connection.onclose = function() { - connection.onclose = null; - command({ name: "notify-connection-closed", id: connection.id }); - }; - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - if (connection.state === "closed") { - aResolve(); - return; - } - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "The connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -window.addEventListener("message", function onMessage(evt) { - if (evt.data === "startConnection") { - testStartRequest().then( - function () { - command({ name: "connection-connected", id: connection.id }); - } - ); - } - else if (evt.data === "closeConnection") { - testCloseConnection().then( - function () { - command({ name: "connection-closed", id: connection.id }); - } - ); - } -}, false); - -</script> -</head> -<body> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html b/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html deleted file mode 100644 index 369621cee..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_sandboxed_presentation.html +++ /dev/null @@ -1,114 +0,0 @@ - -<!DOCTYPE HTML> -<html> -<head> -<meta charset="utf-8"> -<title>Test allow-presentation sandboxing flag</title> -<script type="application/javascript;version=1.8"> - -"use strict"; - -function is(a, b, msg) { - window.parent.postMessage((a === b ? "OK " : "KO ") + msg, "*"); -} - -function ok(a, msg) { - window.parent.postMessage((a ? "OK " : "KO ") + msg, "*"); -} - -function info(msg) { - window.parent.postMessage("INFO " + msg, "*"); -} - -function command(msg) { - window.parent.postMessage("COMMAND " + JSON.stringify(msg), "*"); -} - -function finish() { - window.parent.postMessage("DONE", "*"); -} - -function testGetAvailability() { - return new Promise(function(aResolve, aReject) { - ok(navigator.presentation, "navigator.presentation should be available."); - var request = new PresentationRequest("http://example.com"); - - request.getAvailability().then( - function(aAvailability) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function testStartRequest() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("http://example.com"); - - request.start().then( - function(aAvailability) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function testDefaultRequest() { - return new Promise(function(aResolve, aReject) { - navigator.presentation.defaultRequest = new PresentationRequest("http://example.com"); - is(navigator.presentation.defaultRequest, null, "DefaultRequest shoud be null."); - aResolve(); - }); -} - -function testReconnectRequest() { - return new Promise(function(aResolve, aReject) { - var request = new PresentationRequest("http://example.com"); - - request.reconnect("dummyId").then( - function(aConnection) { - ok(false, "Unexpected success, should get a security error."); - aReject(); - }, - function(aError) { - is(aError.name, "SecurityError", "Should get a security error."); - aResolve(); - } - ); - }); -} - -function runTest() { - testGetAvailability() - .then(testStartRequest) - .then(testDefaultRequest) - .then(testReconnectRequest) - .then(finish); -} - -window.addEventListener("message", function onMessage(evt) { - window.removeEventListener("message", onMessage); - if (evt.data === "start") { - runTest(); - } -}, false); - -window.setTimeout(function() { - command("ready-to-start"); -}, 3000); - -</script> -</head> -<body> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_terminate.html b/dom/presentation/tests/mochitest/file_presentation_terminate.html deleted file mode 100644 index a26a44b90..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_terminate.html +++ /dev/null @@ -1,104 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <head> - <meta charset='utf-8'> - <title>Test for B2G PresentationReceiver at receiver side</title> - </head> - <body> - <div id='content'></div> -<script type='application/javascript;version=1.7'> - -'use strict'; - -function is(a, b, msg) { - if (a === b) { - alert('OK ' + msg); - } else { - alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); - } -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(name, data) { - alert('COMMAND ' + JSON.stringify({name: name, data: data})); -} - -function finish() { - alert('DONE'); -} - -var connection; - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionAvailable ---'); - ok(navigator.presentation, 'Receiver: navigator.presentation should be available.'); - ok(navigator.presentation.receiver, 'Receiver: navigator.presentation.receiver should be available.'); - - navigator.presentation.receiver.connectionList - .then((aList) => { - is(aList.connections.length, 1, 'Should get one conncetion.'); - connection = aList.connections[0]; - ok(connection.id, 'Connection ID should be set: ' + connection.id); - is(connection.state, 'connected', 'Connection state at receiver side should be connected.'); - aResolve(); - }) - .catch((aError) => { - ok(false, 'Receiver: Error occurred when getting the connection: ' + aError); - finish(); - aReject(); - }); - }); -} - -function testConnectionReady() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionReady ---'); - connection.onconnect = function() { - connection.onconnect = null; - ok(false, 'Should not get |onconnect| event.') - aReject(); - }; - if (connection.state === 'connected') { - connection.onconnect = null; - is(connection.state, 'connected', 'Receiver: Connection state should become connected.'); - aResolve(); - } - }); -} - -function testConnectionTerminate() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionTerminate ---'); - connection.onterminate = function() { - connection.onterminate = null; - // Using window.alert at this stage will cause window.close() fail. - // Only trigger it if verdict fail. - if (connection.state !== 'terminated') { - is(connection.state, 'terminated', 'Receiver: Connection should be terminated.'); - } - aResolve(); - }; - command('forward-command', JSON.stringify({ name: 'ready-to-terminate' })); - }); -} - -function runTests() { - testConnectionAvailable() - .then(testConnectionReady) - .then(testConnectionTerminate) -} - -runTests(); - -</script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html b/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html deleted file mode 100644 index d8df8a1a6..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_terminate_establish_connection_error.html +++ /dev/null @@ -1,114 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <head> - <meta charset='utf-8'> - <title>Test for B2G PresentationReceiver at receiver side</title> - </head> - <body> - <div id='content'></div> -<script type='application/javascript;version=1.7'> - -'use strict'; - -function is(a, b, msg) { - if (a === b) { - alert('OK ' + msg); - } else { - alert('KO ' + msg + ' | reason: ' + a + ' != ' + b); - } -} - -function ok(a, msg) { - alert((a ? 'OK ' : 'KO ') + msg); -} - -function info(msg) { - alert('INFO ' + msg); -} - -function command(name, data) { - alert('COMMAND ' + JSON.stringify({name: name, data: data})); -} - -function finish() { - alert('DONE'); -} - -var connection; - -function testConnectionAvailable() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionAvailable ---'); - ok(navigator.presentation, 'Receiver: navigator.presentation should be available.'); - ok(navigator.presentation.receiver, 'Receiver: navigator.presentation.receiver should be available.'); - - navigator.presentation.receiver.connectionList - .then((aList) => { - is(aList.connections.length, 1, 'Should get one connection.'); - connection = aList.connections[0]; - ok(connection.id, 'Connection ID should be set: ' + connection.id); - is(connection.state, 'connected', 'Connection state at receiver side should be connected.'); - aResolve(); - }) - .catch((aError) => { - ok(false, 'Receiver: Error occurred when getting the connection: ' + aError); - finish(); - aReject(); - }); - }); -} - -function testConnectionReady() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionReady ---'); - connection.onconnect = function() { - connection.onconnect = null; - ok(false, 'Should not get |onconnect| event.') - aReject(); - }; - if (connection.state === 'connected') { - connection.onconnect = null; - is(connection.state, 'connected', 'Receiver: Connection state should become connected.'); - aResolve(); - } - }); -} - -function testConnectionTerminate() { - return new Promise(function(aResolve, aReject) { - info('Receiver: --- testConnectionTerminate ---'); - connection.onterminate = function() { - connection.onterminate = null; - // Using window.alert at this stage will cause window.close() fail. - // Only trigger it if verdict fail. - if (connection.state !== 'terminated') { - is(connection.state, 'terminated', 'Receiver: Connection should be terminated.'); - } - aResolve(); - }; - - window.addEventListener('hashchange', function hashchangeHandler(evt) { - var message = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); - if (message.type === 'ready-to-terminate') { - info('Receiver: --- ready-to-terminate ---'); - connection.terminate(); - } - }); - - - command('forward-command', JSON.stringify({ name: 'prepare-for-terminate' })); - }); -} - -function runTests() { - testConnectionAvailable() - .then(testConnectionReady) - .then(testConnectionTerminate) -} - -runTests(); - -</script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test deleted file mode 100644 index 8b1378917..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test +++ /dev/null @@ -1 +0,0 @@ - diff --git a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ b/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ deleted file mode 100644 index fc044e3c4..000000000 --- a/dom/presentation/tests/mochitest/file_presentation_unknown_content_type.test^headers^ +++ /dev/null @@ -1 +0,0 @@ -Content-Type: application/unknown diff --git a/dom/presentation/tests/mochitest/mochitest.ini b/dom/presentation/tests/mochitest/mochitest.ini deleted file mode 100644 index f96e07f1e..000000000 --- a/dom/presentation/tests/mochitest/mochitest.ini +++ /dev/null @@ -1,77 +0,0 @@ -[DEFAULT] -support-files = - PresentationDeviceInfoChromeScript.js - PresentationSessionChromeScript.js - PresentationSessionFrameScript.js - PresentationSessionChromeScript1UA.js - file_presentation_1ua_receiver.html - test_presentation_1ua_sender_and_receiver.js - file_presentation_non_receiver_inner_iframe.html - file_presentation_non_receiver.html - file_presentation_receiver.html - file_presentation_receiver_establish_connection_error.html - file_presentation_receiver_inner_iframe.html - file_presentation_1ua_wentaway.html - test_presentation_1ua_connection_wentaway.js - file_presentation_receiver_auxiliary_navigation.html - test_presentation_receiver_auxiliary_navigation.js - file_presentation_sandboxed_presentation.html - file_presentation_terminate.html - test_presentation_terminate.js - file_presentation_terminate_establish_connection_error.html - test_presentation_terminate_establish_connection_error.js - file_presentation_reconnect.html - file_presentation_unknown_content_type.test - file_presentation_unknown_content_type.test^headers^ - test_presentation_tcp_receiver_establish_connection_unknown_content_type.js - file_presentation_mixed_security_contexts.html - -[test_presentation_dc_sender.html] -[test_presentation_dc_receiver.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_dc_receiver_oop.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_1ua_sender_and_receiver_inproc.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_1ua_sender_and_receiver_oop.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_1ua_connection_wentaway_inproc.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_1ua_connection_wentaway_oop.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_device_info_permission.html] -[test_presentation_tcp_sender_disconnect.html] -skip-if = toolkit == 'android' # Bug 1129785 -[test_presentation_tcp_sender_establish_connection_error.html] -skip-if = toolkit == 'android' # Bug 1129785 -[test_presentation_tcp_receiver_establish_connection_error.html] -skip-if = (e10s || toolkit == 'android' || os == 'mac' || os == 'win') # Bug 1129785, Bug 1204709 -[test_presentation_tcp_receiver_establish_connection_timeout.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_tcp_receiver.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_tcp_receiver_oop.html] -skip-if = (e10s || toolkit == 'android') # Bug 1129785 -[test_presentation_receiver_auxiliary_navigation_inproc.html] -skip-if = e10s -[test_presentation_receiver_auxiliary_navigation_oop.html] -skip-if = e10s -[test_presentation_terminate_inproc.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_terminate_oop.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_terminate_establish_connection_error_inproc.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_terminate_establish_connection_error_oop.html] -skip-if = (e10s || toolkit == 'android') -[test_presentation_sender_on_terminate_request.html] -skip-if = toolkit == 'android' -[test_presentation_sandboxed_presentation.html] -skip-if = true # bug 1315867 -[test_presentation_reconnect.html] -[test_presentation_mixed_security_contexts.html] -[test_presentation_availability.html] diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js deleted file mode 100644 index dbeb4ffcc..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway.js +++ /dev/null @@ -1,175 +0,0 @@ -'use strict'; - -SimpleTest.waitForExplicitFinish(); -SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); - -function debug(str) { - // info(str); -} - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_1ua_wentaway.html'); -var request; -var connection; -var receiverIframe; - -function setup() { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - debug('Got message: device-prompt'); - gScript.removeMessageListener('device-prompt', devicePromptHandler); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - gScript.sendAsyncMessage("trigger-control-channel-open"); - }); - - gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { - debug('Got message: sender-launch'); - gScript.removeMessageListener('sender-launch', senderLaunchHandler); - is(url, receiverUrl, 'Receiver: should receive the same url'); - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute("mozbrowser", "true"); - receiverIframe.setAttribute("mozpresentation", receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - receiverIframe.setAttribute("remote", oop); - - receiverIframe.setAttribute('src', receiverUrl); - receiverIframe.addEventListener("mozbrowserloadend", function mozbrowserloadendHander() { - receiverIframe.removeEventListener("mozbrowserloadend", mozbrowserloadendHander); - info("Receiver loaded."); - }); - - // This event is triggered when the iframe calls "alert". - receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { - var message = evt.detail.message; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - receiverIframe.removeEventListener("mozbrowsershowmodalprompt", - receiverListener); - teardown(); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - aResolve(receiverIframe); - }); - - var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - obs.notifyObservers(promise, 'setup-request-promise', null); - }); - - gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { - debug('Got message: promise-setup-ready'); - gScript.removeMessageListener('promise-setup-ready', - promiseSetupReadyHandler); - gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); - }); - - return Promise.resolve(); -} - -function testCreateRequest() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testCreateRequest ---'); - request = new PresentationRequest(receiverUrl); - request.getAvailability().then((aAvailability) => { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Sender: Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }).catch((aError) => { - ok(false, "Sender: Error occurred when getting availability: " + aError); - teardown(); - aReject(); - }); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - request.start().then((aConnection) => { - connection = aConnection; - ok(connection, "Sender: Connection should be available."); - ok(connection.id, "Sender: Connection ID should be set."); - is(connection.state, "connecting", "Sender: The initial state should be connecting."); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }).catch((aError) => { - ok(false, "Sender: Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - }); - }); -} - -function testConnectionWentaway() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testConnectionWentaway ---'); - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Sender: Connection should be closed."); - receiverIframe.addEventListener('mozbrowserclose', function closeHandler() { - ok(false, 'wentaway should not trigger receiver close'); - aResolve(); - }); - setTimeout(aResolve, 3000); - }; - gScript.addMessageListener('ready-to-remove-receiverFrame', function onReadyToRemove() { - gScript.removeMessageListener('ready-to-remove-receiverFrame', onReadyToRemove); - receiverIframe.src = "http://example.com"; - }); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - debug('Got message: teardown-complete'); - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup().then(testCreateRequest) - .then(testStartConnection) - .then(testConnectionWentaway) - .then(teardown); -} - -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: "browser", allow: true, context: document}, -], () => { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.test.enabled", true], - ["dom.mozBrowserFramesEnabled", true], - ["dom.ipc.tabs.disabled", false], - ["network.disable.ipc.security", true], - ["dom.presentation.test.stage", 0]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html deleted file mode 100644 index 68491d81b..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_inproc.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1258600"> - Test for PresentationConnectionCloseEvent with wentaway reason</a> - <script type="application/javascript;version=1.8" src="test_presentation_1ua_connection_wentaway.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html b/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html deleted file mode 100644 index 68491d81b..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_connection_wentaway_oop.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1258600"> - Test for PresentationConnectionCloseEvent with wentaway reason</a> - <script type="application/javascript;version=1.8" src="test_presentation_1ua_connection_wentaway.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js deleted file mode 100644 index 8a7787b40..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver.js +++ /dev/null @@ -1,370 +0,0 @@ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* 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/. */ - -'use strict'; - -function debug(str) { - // info(str); -} - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_1ua_receiver.html'); -var request; -var connection; -var receiverIframe; -var presentationId; -const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0]; -const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length); -const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER); -TYPED_DATA_ARRAY.set(DATA_ARRAY); - -function postMessageToIframe(aType) { - receiverIframe.src = receiverUrl + "#" + - encodeURIComponent(JSON.stringify({ type: aType })); -} - -function setup() { - - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - debug('Got message: device-prompt'); - gScript.removeMessageListener('device-prompt', devicePromptHandler); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - gScript.sendAsyncMessage("trigger-control-channel-open"); - }); - - gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { - debug('Got message: sender-launch'); - gScript.removeMessageListener('sender-launch', senderLaunchHandler); - is(url, receiverUrl, 'Receiver: should receive the same url'); - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('src', receiverUrl); - receiverIframe.setAttribute("mozbrowser", "true"); - receiverIframe.setAttribute("mozpresentation", receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - receiverIframe.setAttribute("remote", oop); - - // This event is triggered when the iframe calls "alert". - receiverIframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { - var message = evt.detail.message; - debug('Got iframe message: ' + message); - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - receiverIframe.removeEventListener("mozbrowsershowmodalprompt", - receiverListener); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - aResolve(receiverIframe); - }); - - var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - obs.notifyObservers(promise, 'setup-request-promise', null); - }); - - gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { - debug('Got message: promise-setup-ready'); - gScript.removeMessageListener('promise-setup-ready', promiseSetupReadyHandler); - gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); - }); - - return Promise.resolve(); -} - -function testCreateRequest() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testCreateRequest ---'); - request = new PresentationRequest("file_presentation_1ua_receiver.html"); - request.getAvailability().then((aAvailability) => { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Sender: Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }).catch((aError) => { - ok(false, "Sender: Error occurred when getting availability: " + aError); - teardown(); - aReject(); - }); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - request.start().then((aConnection) => { - connection = aConnection; - ok(connection, "Sender: Connection should be available."); - ok(connection.id, "Sender: Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - is(connection.url, receiverUrl, "request URL should be expanded to absolute URL"); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - presentationId = connection.id; - aResolve(); - }; - }).catch((aError) => { - ok(false, "Sender: Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - }); - - let request2 = new PresentationRequest("/"); - request2.start().then(() => { - ok(false, "Sender: session start should fail while there is an unsettled promise."); - }).catch((aError) => { - is(aError.name, "OperationError", "Expect to get OperationError."); - }); - }); -} - -function testSendMessage() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testSendMessage ---'); - gScript.addMessageListener('trigger-message-from-sender', function triggerMessageFromSenderHandler() { - debug('Got message: trigger-message-from-sender'); - gScript.removeMessageListener('trigger-message-from-sender', triggerMessageFromSenderHandler); - info('Send message to receiver'); - connection.send('msg-sender-to-receiver'); - }); - - gScript.addMessageListener('message-from-sender-received', function messageFromSenderReceivedHandler() { - debug('Got message: message-from-sender-received'); - gScript.removeMessageListener('message-from-sender-received', messageFromSenderReceivedHandler); - aResolve(); - }); - }); -} - -function testIncomingMessage() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testIncomingMessage ---'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let msg = evt.data; - is(msg, "msg-receiver-to-sender", "Sender: Sender should receive message from Receiver"); - postMessageToIframe('message-from-receiver-received'); - aResolve(); - }); - postMessageToIframe('trigger-message-from-receiver'); - }); -} - -function testSendBlobMessage() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testSendBlobMessage ---'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let msg = evt.data; - is(msg, "testIncomingBlobMessage", "Sender: Sender should receive message from Receiver"); - let blob = new Blob(["Hello World"], {type : 'text/plain'}); - connection.send(blob); - aResolve(); - }); - }); -} - -function testSendArrayBuffer() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testSendArrayBuffer ---'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let msg = evt.data; - is(msg, "testIncomingArrayBuffer", "Sender: Sender should receive message from Receiver"); - connection.send(DATA_ARRAY_BUFFER); - aResolve(); - }); - }); -} - -function testSendArrayBufferView() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testSendArrayBufferView ---'); - connection.addEventListener('message', function messageHandler(evt) { - connection.removeEventListener('message', messageHandler); - let msg = evt.data; - is(msg, "testIncomingArrayBufferView", "Sender: Sender should receive message from Receiver"); - connection.send(TYPED_DATA_ARRAY); - aResolve(); - }); - }); -} - -function testCloseConnection() { - info('Sender: --- testCloseConnection ---'); - // Test terminate immediate after close. - function controlChannelEstablishedHandler() - { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - ok(false, "terminate after close should do nothing"); - } - gScript.addMessageListener('ready-to-close', function onReadyToClose() { - gScript.removeMessageListener('ready-to-close', onReadyToClose); - connection.close(); - - gScript.addMessageListener('control-channel-established', controlChannelEstablishedHandler); - connection.terminate(); - }); - - return Promise.all([ - new Promise(function(aResolve, aReject) { - connection.onclose = function() { - connection.onclose = null; - is(connection.state, 'closed', 'Sender: Connection should be closed.'); - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - aResolve(); - }; - }), - new Promise(function(aResolve, aReject) { - let timeout = setTimeout(function() { - gScript.removeMessageListener('device-disconnected', - deviceDisconnectedHandler); - ok(true, "terminate after close should not trigger device.disconnect"); - aResolve(); - }, 3000); - - function deviceDisconnectedHandler() { - gScript.removeMessageListener('device-disconnected', - deviceDisconnectedHandler); - ok(false, "terminate after close should not trigger device.disconnect"); - clearTimeout(timeout); - aResolve(); - } - - gScript.addMessageListener('device-disconnected', deviceDisconnectedHandler); - }), - new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-closed', function onReceiverClosed() { - gScript.removeMessageListener('receiver-closed', onReceiverClosed); - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - aResolve(); - }); - }), - ]); -} - -function testTerminateAfterClose() { - info('Sender: --- testTerminateAfterClose ---'); - return Promise.race([ - new Promise(function(aResolve, aReject) { - connection.onterminate = function() { - connection.onterminate = null; - ok(false, 'terminate after close should do nothing'); - aResolve(); - }; - connection.terminate(); - }), - new Promise(function(aResolve, aReject) { - setTimeout(function() { - is(connection.state, 'closed', 'Sender: Connection should be closed.'); - aResolve(); - }, 3000); - }), - ]); -} - -function testReconnect() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testReconnect ---'); - gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablished); - gScript.sendAsyncMessage("trigger-control-channel-open"); - }); - - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - debug('Got message: start-reconnect'); - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - is(url, receiverUrl, "URLs should be the same.") - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - gScript.addMessageListener('ready-to-reconnect', function onReadyToReconnect() { - gScript.removeMessageListener('ready-to-reconnect', onReadyToReconnect); - request.reconnect(presentationId).then((aConnection) => { - connection = aConnection; - ok(connection, "Sender: Connection should be available."); - is(connection.id, presentationId, "The presentationId should be the same."); - is(connection.state, "connecting", "The initial state should be connecting."); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }).catch((aError) => { - ok(false, "Sender: Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - }); - }); - - postMessageToIframe('prepare-for-reconnect'); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - debug('Got message: teardown-complete'); - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup().then(testCreateRequest) - .then(testStartConnection) - .then(testSendMessage) - .then(testIncomingMessage) - .then(testSendBlobMessage) - .then(testCloseConnection) - .then(testReconnect) - .then(testSendArrayBuffer) - .then(testSendArrayBufferView) - .then(testCloseConnection) - .then(testTerminateAfterClose) - .then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: "browser", allow: true, context: document}, -], () => { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - /* Mocked TCP session transport builder in the test */ - ["dom.presentation.session_transport.data_channel.enable", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.test.enabled", true], - ["dom.presentation.test.stage", 0], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true], - ["media.navigator.permission.disabled", true]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html deleted file mode 100644 index 520b1a98c..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_inproc.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1234492"> - Test for B2G Presentation API when sender and receiver at the same side</a> - <script type="application/javascript;version=1.8" src="test_presentation_1ua_sender_and_receiver.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html deleted file mode 100644 index e744e6802..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_1ua_sender_and_receiver_oop.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side (OOP ver.)</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1234492"> - Test for B2G Presentation API when sender and receiver at the same side (OOP ver.)</a> - <script type="application/javascript;version=1.8" src="test_presentation_1ua_sender_and_receiver.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_availability.html b/dom/presentation/tests/mochitest/test_presentation_availability.html deleted file mode 100644 index 89f1ad1b7..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_availability.html +++ /dev/null @@ -1,236 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for PresentationAvailability</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1228508">Test PresentationAvailability</a> -<script type="application/javascript;version=1.8"> - -"use strict"; - -var testDevice = { - id: 'id', - name: 'name', - type: 'type', -}; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationDeviceInfoChromeScript.js')); -var request; -var availability; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('setup-complete', function() { - aResolve(); - }); - gScript.sendAsyncMessage('setup'); - }); -} - -function testInitialUnavailable() { - request = new PresentationRequest("https://example.com"); - - return request.getAvailability().then(function(aAvailability) { - is(aAvailability.value, false, "Should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - } - availability = aAvailability; - gScript.sendAsyncMessage('trigger-device-add', testDevice); - }).catch(function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - }); -} - -function testInitialAvailable() { - let anotherRequest = new PresentationRequest("https://example.net"); - return anotherRequest.getAvailability().then(function(aAvailability) { - is(aAvailability.value, true, "Should have available device initially"); - isnot(aAvailability, availability, "Should get different availability object for different request URL"); - }).catch(function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - }); -} - -function testSameObject() { - let sameUrlRequest = new PresentationRequest("https://example.com"); - return sameUrlRequest.getAvailability().then(function(aAvailability) { - is(aAvailability, availability, "Should get same availability object for same request URL"); - }).catch(function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - }); -} - -function testOnChangeEvent() { - return new Promise(function(aResolve, aReject) { - availability.onchange = function() { - availability.onchange = null; - is(availability.value, false, "Should have no available device after device removed"); - aResolve(); - } - gScript.sendAsyncMessage('trigger-device-remove'); - }); -} - -function testConsecutiveGetAvailability() { - let request = new PresentationRequest("https://example.org"); - let firstAvailabilityResolved = false; - return Promise.all([ - request.getAvailability().then(function() { - firstAvailabilityResolved = true; - }), - request.getAvailability().then(function() { - ok(firstAvailabilityResolved, "getAvailability() should be resolved in sequence"); - }) - ]).catch(function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - }); -} - -function testUnsupportedDeviceAvailability() { - return Promise.race([ - new Promise(function(aResolve, aReject) { - let request = new PresentationRequest("https://test.com"); - request.getAvailability().then(function(aAvailability) { - availability = aAvailability; - aAvailability.onchange = function() { - availability.onchange = null; - ok(false, "Should not get onchange event."); - teardown(); - } - }); - gScript.sendAsyncMessage('trigger-add-unsupport-url-device'); - }), - new Promise(function(aResolve, aReject) { - setTimeout(function() { - ok(true, "Should not get onchange event."); - availability.onchange = null; - gScript.sendAsyncMessage('trigger-remove-unsupported-device'); - aResolve(); - }, 3000); - }), - ]); -} - -function testMultipleAvailabilityURLs() { - let request1 = new PresentationRequest(["https://example.com", - "https://example1.com"]); - let request2 = new PresentationRequest(["https://example1.com", - "https://example2.com"]); - return Promise.all([ - request1.getAvailability().then(function(aAvailability) { - return new Promise(function(aResolve) { - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(true, "Should get onchange event."); - aResolve(); - }; - }); - }), - request2.getAvailability().then(function(aAvailability) { - return new Promise(function(aResolve) { - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(true, "Should get onchange event."); - aResolve(); - }; - }); - }), - new Promise(function(aResolve) { - gScript.sendAsyncMessage('trigger-add-multiple-devices'); - aResolve(); - }), - ]).then(new Promise(function(aResolve) { - gScript.sendAsyncMessage('trigger-remove-multiple-devices'); - aResolve(); - })); -} - -function testPartialSupportedDeviceAvailability() { - let request1 = new PresentationRequest(["https://supportedUrl.com"]); - let request2 = new PresentationRequest(["http://notSupportedUrl.com"]); - - return Promise.all([ - request1.getAvailability().then(function(aAvailability) { - return new Promise(function(aResolve) { - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(true, "Should get onchange event."); - aResolve(); - }; - }); - }), - Promise.race([ - request2.getAvailability().then(function(aAvailability) { - return new Promise(function(aResolve) { - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(false, "Should get onchange event."); - aResolve(); - }; - }); - }), - new Promise(function(aResolve) { - setTimeout(function() { - ok(true, "Should not get onchange event."); - availability.onchange = null; - aResolve(); - }, 3000); - }), - ]), - new Promise(function(aResolve) { - gScript.sendAsyncMessage('trigger-add-https-devices'); - aResolve(); - }), - ]).then(new Promise(function(aResolve) { - gScript.sendAsyncMessage('trigger-remove-https-devices'); - aResolve(); - })); -} - -function teardown() { - request = null; - availability = null; - gScript.sendAsyncMessage('teardown'); - gScript.destroy(); - SimpleTest.finish(); -} - -function runTests() { - ok(navigator.presentation, "navigator.presentation should be available."); - testSetup().then(testInitialUnavailable) - .then(testInitialAvailable) - .then(testSameObject) - .then(testOnChangeEvent) - .then(testConsecutiveGetAvailability) - .then(testMultipleAvailabilityURLs) - .then(testUnsupportedDeviceAvailability) - .then(testPartialSupportedDeviceAvailability) - .then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); -SpecialPowers.pushPermissions([ - {type: "presentation-device-manage", allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html b/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html deleted file mode 100644 index 89a51afb7..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_datachannel_sessiontransport.html +++ /dev/null @@ -1,245 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for data channel as session transport in Presentation API</title> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test for data channel as session transport in Presentation API</a> -<div id="content" style="display: none"> - -</div> -<pre id="test"> -<script class="testbody" type="text/javascript"> - -"use strict"; - -SimpleTest.waitForExplicitFinish(); - -const loadingTimeoutPref = "presentation.receiver.loading.timeout"; - -var clientBuilder; -var serverBuilder; -var clientTransport; -var serverTransport; - -const clientMessage = "Client Message"; -const serverMessage = "Server Message"; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; -const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -const { Services } = Cu.import("resource://gre/modules/Services.jsm"); - -var isClientReady = false; -var isServerReady = false; -var isClientClosed = false; -var isServerClosed = false; - -var gResolve; -var gReject; - -const clientCallback = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), - notifyTransportReady: function () { - info("Client transport ready."); - - isClientReady = true; - if (isClientReady && isServerReady) { - gResolve(); - } - }, - notifyTransportClosed: function (aReason) { - info("Client transport is closed."); - - isClientClosed = true; - if (isClientClosed && isServerClosed) { - gResolve(); - } - }, - notifyData: function(aData) { - is(aData, serverMessage, "Client transport receives data."); - gResolve(); - }, -}; - -const serverCallback = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), - notifyTransportReady: function () { - info("Server transport ready."); - - isServerReady = true; - if (isClientReady && isServerReady) { - gResolve(); - } - }, - notifyTransportClosed: function (aReason) { - info("Server transport is closed."); - - isServerClosed = true; - if (isClientClosed && isServerClosed) { - gResolve(); - } - }, - notifyData: function(aData) { - is(aData, clientMessage, "Server transport receives data."); - gResolve() - }, -}; - -const clientListener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), - onSessionTransport: function(aTransport) { - info("Client Transport is built."); - clientTransport = aTransport; - clientTransport.callback = clientCallback; - }, - onError: function(aError) { - ok(false, "client's builder reports error " + aError); - }, - sendOffer: function(aOffer) { - setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0); - }, - sendAnswer: function(aAnswer) { - setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0); - }, - sendIceCandidate: function(aCandidate) { - setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0); - }, - disconnect: function(aReason) { - setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0); - setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0); - }, - set remoteBuilder(aRemoteBuilder) { - this._remoteBuilder = aRemoteBuilder; - }, - set localBuilder(aLocalBuilder) { - this._localBuilder = aLocalBuilder; - }, -} - -const serverListener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), - onSessionTransport: function(aTransport) { - info("Server Transport is built."); - serverTransport = aTransport; - serverTransport.callback = serverCallback; - serverTransport.enableDataNotification(); - }, - onError: function(aError) { - ok(false, "server's builder reports error " + aError); - }, - sendOffer: function(aOffer) { - setTimeout(()=>this._remoteBuilder.onOffer(aOffer), 0); - }, - sendAnswer: function(aAnswer) { - setTimeout(()=>this._remoteBuilder.onAnswer(aAnswer), 0); - }, - sendIceCandidate: function(aCandidate) { - setTimeout(()=>this._remoteBuilder.onIceCandidate(aCandidate), 0); - }, - disconnect: function(aReason) { - setTimeout(()=>this._localBuilder.notifyDisconnected(aReason), 0); - setTimeout(()=>this._remoteBuilder.notifyDisconnected(aReason), 0); - }, - set remoteBuilder(aRemoteBuilder) { - this._remoteBuilder = aRemoteBuilder; - }, - set localBuilder(aLocalBuilder) { - this._localBuilder = aLocalBuilder; - }, -} - -function testBuilder() { - return new Promise(function(aResolve, aReject) { - gResolve = aResolve; - gReject = aReject; - - clientBuilder = Cc["@mozilla.org/presentation/datachanneltransportbuilder;1"] - .createInstance(Ci.nsIPresentationDataChannelSessionTransportBuilder); - serverBuilder = Cc["@mozilla.org/presentation/datachanneltransportbuilder;1"] - .createInstance(Ci.nsIPresentationDataChannelSessionTransportBuilder); - - clientListener.localBuilder = clientBuilder; - clientListener.remoteBuilder = serverBuilder; - serverListener.localBuilder = serverBuilder; - serverListener.remoteBuilder = clientBuilder; - - clientBuilder - .buildDataChannelTransport(Ci.nsIPresentationService.ROLE_CONTROLLER, - window, - clientListener); - - serverBuilder - .buildDataChannelTransport(Ci.nsIPresentationService.ROLE_RECEIVER, - window, - serverListener); - }); -} - -function testClientSendMessage() { - return new Promise(function(aResolve, aReject) { - info("client sends message"); - gResolve = aResolve; - gReject = aReject; - - clientTransport.send(clientMessage); - }); -} - -function testServerSendMessage() { - return new Promise(function(aResolve, aReject) { - info("server sends message"); - gResolve = aResolve; - gReject = aReject; - - serverTransport.send(serverMessage); - setTimeout(()=>clientTransport.enableDataNotification(), 0); - }); -} - -function testCloseSessionTransport() { - return new Promise(function(aResolve, aReject) { - info("close session transport"); - gResolve = aResolve; - gReject = aReject; - - serverTransport.close(Cr.NS_OK); - }); -} - -function finish() { - info("test finished, teardown"); - Services.prefs.clearUserPref(loadingTimeoutPref); - - SimpleTest.finish(); -} - -function error(aError) { - ok(false, "report Error " + aError.name + ":" + aError.message); - gReject(); -} - -function runTests() { - Services.prefs.setIntPref(loadingTimeoutPref, 30000); - - testBuilder() - .then(testClientSendMessage) - .then(testServerSendMessage) - .then(testCloseSessionTransport) - .then(finish) - .catch(error); - -} - -window.addEventListener("load", function() { - runTests(); -}); - -</script> -</pre> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html deleted file mode 100644 index a42489bdb..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html +++ /dev/null @@ -1,141 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationConnection API at receiver side</title> - <script type="application/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=1148307">Test for B2G PresentationConnection API at receiver side</a> -<p id="display"></p> -<div id="content" style="display: none"></div> -<pre id="test"></pre> -<script type="application/javascript"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - var iframe = document.createElement('iframe'); - iframe.setAttribute('src', receiverUrl); - iframe.setAttribute("mozbrowser", "true"); - iframe.setAttribute("mozpresentation", receiverUrl); - - // This event is triggered when the iframe calls "alert". - iframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { - var message = evt.detail.message; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - iframe.removeEventListener("mozbrowsershowmodalprompt", - receiverListener); - teardown(); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(iframe); - - aResolve(iframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - gScript.addMessageListener('offer-received', function offerReceivedHandler() { - gScript.removeMessageListener('offer-received', offerReceivedHandler); - info("An offer is received."); - }); - - gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { - gScript.removeMessageListener('answer-sent', answerSentHandler); - ok(aIsValid, "A valid answer is sent."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); - }); - - gScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { - gScript.removeMessageListener('check-navigator', checknavigatorHandler); - ok(aSuccess, "buildDataChannel get correct window object"); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); - }); - - aResolve(); - }); -} - -function testIncomingSessionRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - - ok(navigator.presentation, "navigator.presentation should be available in in-process pages."); - is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance."); - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: "browser", allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", false], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", true], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html deleted file mode 100644 index b289b0be6..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html +++ /dev/null @@ -1,213 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationConnection API at receiver side (OOP)</title> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="PresentationSessionFrameScript.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=1148307">Test B2G PresentationConnection API at receiver side (OOP)</a> -<p id="display"></p> -<div id="content" style="display: none"></div> -<pre id="test"></pre> -<script type="application/javascript"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); -var nonReceiverUrl = SimpleTest.getTestFileURL('file_presentation_non_receiver.html'); - -var isReceiverFinished = false; -var isNonReceiverFinished = false; - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); -var receiverIframe; - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - // Create a receiver OOP iframe. - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('remote', 'true'); - receiverIframe.setAttribute('mozbrowser', 'true'); - receiverIframe.setAttribute('mozpresentation', receiverUrl); - receiverIframe.setAttribute('src', receiverUrl); - - // This event is triggered when the iframe calls "alert". - receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - if (command.name == "trigger-incoming-message") { - var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); - mm.sendAsyncMessage('trigger-incoming-message', {"data": command.data}); - } else { - gScript.sendAsyncMessage(command.name, command.data); - } - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - receiverIframe.removeEventListener('mozbrowsershowmodalprompt', receiverListener); - - isReceiverFinished = true; - - if (isNonReceiverFinished) { - teardown(); - } - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - receiverIframe.addEventListener("mozbrowserloadstart", function onLoadEnd() { - receiverIframe.removeEventListener("mozbrowserloadstart", onLoadEnd); - var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); - mm.loadFrameScript("data:,(" + loadPrivilegedScriptTest.toString() + ")();", false); - }); - - aResolve(receiverIframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - // Create a non-receiver OOP iframe. - var nonReceiverIframe = document.createElement('iframe'); - nonReceiverIframe.setAttribute('remote', 'true'); - nonReceiverIframe.setAttribute('mozbrowser', 'true'); - nonReceiverIframe.setAttribute('src', nonReceiverUrl); - - // This event is triggered when the iframe calls "alert". - nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - nonReceiverIframe.removeEventListener('mozbrowsershowmodalprompt', nonReceiverListener); - - isNonReceiverFinished = true; - - if (isReceiverFinished) { - teardown(); - } - } - }, false); - - document.body.appendChild(nonReceiverIframe); - - gScript.addMessageListener('offer-received', function offerReceivedHandler() { - gScript.removeMessageListener('offer-received', offerReceivedHandler); - info("An offer is received."); - }); - - gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { - gScript.removeMessageListener('answer-sent', answerSentHandler); - ok(aIsValid, "A valid answer is sent."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); - }); - - var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); - mm.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { - mm.removeMessageListener('check-navigator', checknavigatorHandler); - ok(aSuccess.data.data, "buildDataChannel get correct window object"); - }); - - mm.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - mm.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - mm.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - mm.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - is(aReason.data.data, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); - }); - - aResolve(); - }); -} - -function testIncomingSessionRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - }); -} - -var mmTeardownComplete = false; -var gScriptTeardownComplete = false; -function teardown() { - var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe); - mm.addMessageListener('teardown-complete', function teardownCompleteHandler() { - mm.removeMessageListener('teardown-complete', teardownCompleteHandler); - mmTeardownComplete = true; - if (gScriptTeardownComplete) { - SimpleTest.finish(); - } - }); - - mm.sendAsyncMessage('teardown'); - - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - gScriptTeardownComplete = true; - if (mmTeardownComplete) { - SimpleTest.finish(); - } - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", false], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", true], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true], - ["dom.ipc.browser_frames.oop_by_default", true], - ["presentation.receiver.loading.timeout", 5000000]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html deleted file mode 100644 index 97e252e84..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html +++ /dev/null @@ -1,291 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="PresentationSessionFrameScript.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1148307">Test for B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var frameScript = SpecialPowers.isMainProcess() ? gScript : contentScript; -var request; -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("http://example.com/"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { - frameScript.removeMessageListener('check-navigator', checknavigatorHandler); - ok(aSuccess, "buildDataChannel get correct window object"); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - frameScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - frameScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - }); - - frameScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - frameScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - var connectionFromEvent; - request.onconnectionavailable = function(aEvent) { - request.onconnectionavailable = null; - connectionFromEvent = aEvent.connection; - ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); - - if (connection) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - }; - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - if (connectionFromEvent) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testSend() { - return new Promise(function(aResolve, aReject) { - const outgoingMessage = "test outgoing message"; - - frameScript.addMessageListener('message-sent', function messageSentHandler(aMessage) { - frameScript.removeMessageListener('message-sent', messageSentHandler); - is(aMessage, outgoingMessage, "The message is sent out."); - aResolve(); - }); - - connection.send(outgoingMessage); - }); -} - -function testIncomingMessage() { - return new Promise(function(aResolve, aReject) { - const incomingMessage = "test incoming message"; - - connection.addEventListener('message', function messageHandler(aEvent) { - connection.removeEventListener('message', messageHandler); - is(aEvent.data, incomingMessage, "An incoming message should be received."); - aResolve(); - }); - - frameScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - frameScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - frameScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -function testReconnect() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnect ---'); - gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablished); - gScript.sendAsyncMessage("trigger-control-channel-open"); - }); - - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - is(url, "http://example.com/", "URLs should be the same.") - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { - frameScript.removeMessageListener('check-navigator', checknavigatorHandler); - ok(aSuccess, "buildDataChannel get correct window object"); - }); - - request.reconnect(connection.id).then( - function(aConnection) { - ok(aConnection, "Connection should be available."); - ok(aConnection.id, "Connection ID should be set."); - is(aConnection.state, "connecting", "The initial state should be connecting."); - is(aConnection, connection, "The reconnected connection should be the same."); - - aConnection.onconnect = function() { - aConnection.onconnect = null; - is(aConnection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - info('teardown-complete'); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function testConstructRequestError() { - return Promise.all([ - // XXX: Bug 1305204 - uncomment when bug 1275746 is fixed again. - // new Promise(function(aResolve, aReject) { - // try { - // request = new PresentationRequest("\\\\\\"); - // } - // catch(e) { - // is(e.name, "SyntaxError", "Expect to get SyntaxError."); - // aResolve(); - // } - // }), - new Promise(function(aResolve, aReject) { - try { - request = new PresentationRequest([]); - } - catch(e) { - is(e.name, "NotSupportedError", "Expect to get NotSupportedError."); - aResolve(); - } - }), - ]); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnection). - then(testSend). - then(testIncomingMessage). - then(testCloseConnection). - then(testReconnect). - then(testCloseConnection). - then(testConstructRequestError). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", true]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_device_info.html b/dom/presentation/tests/mochitest/test_presentation_device_info.html deleted file mode 100644 index 77253e41d..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_device_info.html +++ /dev/null @@ -1,144 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G Presentation Device Info API</title> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080474">Test for B2G Presentation Device Info API</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -SimpleTest.waitForExplicitFinish(); - -var testDevice = { - id: 'id', - name: 'name', - type: 'type', -}; - -var gUrl = SimpleTest.getTestFileURL('PresentationDeviceInfoChromeScript.js'); -var gScript = SpecialPowers.loadChromeScript(gUrl); - -function testSetup() { - return new Promise(function(resolve, reject) { - gScript.addMessageListener('setup-complete', function() { - resolve(); - }); - gScript.sendAsyncMessage('setup'); - }); -} - -function testForceDiscovery() { - info('test force discovery'); - return new Promise(function(resolve, reject) { - gScript.addMessageListener('force-discovery', function() { - ok(true, 'nsIPresentationDeviceProvider.forceDiscovery is invoked'); - resolve(); - }); - navigator.mozPresentationDeviceInfo.forceDiscovery(); - }); -} - -function testDeviceAdd() { - info('test device add'); - return new Promise(function(resolve, reject) { - navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { - navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); - let detail = e.detail; - is(detail.type, 'add', 'expected update type'); - is(detail.deviceInfo.id, testDevice.id, 'expected device id'); - is(detail.deviceInfo.name, testDevice.name, 'expected device name'); - is(detail.deviceInfo.type, testDevice.type, 'expected device type'); - - navigator.mozPresentationDeviceInfo.getAll() - .then(function(devices) { - is(devices.length, 1, 'expected 1 available device'); - is(devices[0].id, testDevice.id, 'expected device id'); - is(devices[0].name, testDevice.name, 'expected device name'); - is(devices[0].type, testDevice.type, 'expected device type'); - resolve(); - }); - }); - gScript.sendAsyncMessage('trigger-device-add', testDevice); - }); -} - -function testDeviceUpdate() { - info('test device update'); - return new Promise(function(resolve, reject) { - testDevice.name = 'name-update'; - - navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { - navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); - let detail = e.detail; - is(detail.type, 'update', 'expected update type'); - is(detail.deviceInfo.id, testDevice.id, 'expected device id'); - is(detail.deviceInfo.name, testDevice.name, 'expected device name'); - is(detail.deviceInfo.type, testDevice.type, 'expected device type'); - - navigator.mozPresentationDeviceInfo.getAll() - .then(function(devices) { - is(devices.length, 1, 'expected 1 available device'); - is(devices[0].id, testDevice.id, 'expected device id'); - is(devices[0].name, testDevice.name, 'expected device name'); - is(devices[0].type, testDevice.type, 'expected device type'); - resolve(); - }); - }); - gScript.sendAsyncMessage('trigger-device-update', testDevice); - }); -} - -function testDeviceRemove() { - info('test device remove'); - return new Promise(function(resolve, reject) { - navigator.mozPresentationDeviceInfo.addEventListener('devicechange', function deviceChangeHandler(e) { - navigator.mozPresentationDeviceInfo.removeEventListener('devicechange', deviceChangeHandler); - let detail = e.detail; - is(detail.type, 'remove', 'expected update type'); - is(detail.deviceInfo.id, testDevice.id, 'expected device id'); - is(detail.deviceInfo.name, testDevice.name, 'expected device name'); - is(detail.deviceInfo.type, testDevice.type, 'expected device type'); - - navigator.mozPresentationDeviceInfo.getAll() - .then(function(devices) { - is(devices.length, 0, 'expected 0 available device'); - resolve(); - }); - }); - gScript.sendAsyncMessage('trigger-device-remove'); - }); -} - -function runTests() { - testSetup() - .then(testForceDiscovery) - .then(testDeviceAdd) - .then(testDeviceUpdate) - .then(testDeviceRemove) - .then(function() { - info('test finished, teardown'); - gScript.sendAsyncMessage('teardown', ''); - gScript.destroy(); - SimpleTest.finish(); - }); -} - -window.addEventListener('load', function() { - SpecialPowers.pushPrefEnv({ - 'set': [ - ['dom.presentation.enabled', true], - ] - }, runTests); -}); - -</script> -</pre> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html b/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html deleted file mode 100644 index c7f7ac96d..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_device_info_permission.html +++ /dev/null @@ -1,35 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G Presentation Device Info API Permission</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080474">Test for B2G Presentation Device Info API Permission</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -SimpleTest.waitForExplicitFinish(); - -function runTests() { - is(navigator.mozPresentationDeviceInfo, undefined, 'navigator.mozPresentationDeviceInfo is undefined'); - SimpleTest.finish(); -} - -window.addEventListener('load', function() { - SpecialPowers.pushPrefEnv({ - 'set': [ - ['dom.presentation.enabled', true], - ] - }, runTests); -}); - -</script> -</pre> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html b/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html deleted file mode 100644 index 31918a2c4..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_mixed_security_contexts.html +++ /dev/null @@ -1,81 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test default request for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268758">Test allow-presentation sandboxing flag</a> -<iframe id="iframe" src="https://example.com/tests/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html"></iframe> -<script type="application/javascript;version=1.8"> - -"use strict"; - -var iframe = document.getElementById("iframe"); -var readyToStart = false; -var testSetuped = false; - -function setup() { - SpecialPowers.addPermission("presentation", - true, { url: "https://example.com/tests/dom/presentation/tests/mochitest/file_presentation_mixed_security_contexts.html", - originAttributes: { - appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID, - inIsolatedMozBrowser: false }}); - - return new Promise(function(aResolve, aReject) { - addEventListener("message", function listener(event) { - var message = event.data; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - if (command === "ready-to-start") { - readyToStart = true; - startTest(); - } - } else if (/^DONE$/.exec(message)) { - window.removeEventListener('message', listener); - SimpleTest.finish(); - } - }, false); - - testSetuped = true; - aResolve(); - }); -} - -iframe.onload = startTest(); - -function startTest() { - if (!(testSetuped && readyToStart)) { - return; - } - iframe.contentWindow.postMessage("start", "*"); -} - -function runTests() { - ok(navigator.presentation, "navigator.presentation should be available."); - setup().then(startTest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: "presentation-device-manage", allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js deleted file mode 100644 index 0647bff3a..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("PresentationSessionChromeScript.js")); -var receiverUrl = SimpleTest.getTestFileURL("file_presentation_receiver_auxiliary_navigation.html"); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage("trigger-device-add"); - - var iframe = document.createElement("iframe"); - iframe.setAttribute("mozbrowser", "true"); - iframe.setAttribute("mozpresentation", receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - iframe.setAttribute("remote", oop); - iframe.setAttribute("src", receiverUrl); - - // This event is triggered when the iframe calls "postMessage". - iframe.addEventListener("mozbrowsershowmodalprompt", function listener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - iframe.removeEventListener("mozbrowsershowmodalprompt", listener); - - teardown(); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(iframe); - - aResolve(iframe); - }); - obs.notifyObservers(promise, "setup-request-promise", null); - - aResolve(); - }); -} - -function teardown() { - gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() { - gScript.removeMessageListener("teardown-complete", teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage("teardown"); -} - -function runTests() { - setup().then(); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: "presentation-device-manage", allow: false, context: document}, - {type: "browser", allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html deleted file mode 100644 index f873fa3da..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_inproc.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268810"> - Test for receiver page with sandboxed auxiliary navigation browsing context flag.</a> - <script type="application/javascript;version=1.8" src="test_presentation_receiver_auxiliary_navigation.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html b/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html deleted file mode 100644 index f873fa3da..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_receiver_auxiliary_navigation_oop.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API when sender and receiver at the same side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - </head> - <body> - <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268810"> - Test for receiver page with sandboxed auxiliary navigation browsing context flag.</a> - <script type="application/javascript;version=1.8" src="test_presentation_receiver_auxiliary_navigation.js"> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_reconnect.html b/dom/presentation/tests/mochitest/test_presentation_reconnect.html deleted file mode 100644 index 079b7f5c5..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_reconnect.html +++ /dev/null @@ -1,379 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> - <script type="application/javascript" src="PresentationSessionFrameScript.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1197690">Test for Presentation API at sender side</a> -<iframe id="iframe" src="file_presentation_reconnect.html"></iframe> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var iframe = document.getElementById("iframe"); -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var frameScript = SpecialPowers.isMainProcess() ? gScript : contentScript; -var request; -var connection; -var commandHandler = {}; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - addEventListener("message", function listener(event) { - var message = event.data; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - if (command.name in commandHandler) { - commandHandler[command.name](command); - } - } else if (/^DONE$/.exec(message)) { - window.removeEventListener('message', listener); - SimpleTest.finish(); - } - }, false); - - request = new PresentationRequest("http://example.com/"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - info("The control channel is closed. " + aReason); - }); - - frameScript.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) { - ok(aSuccess, "buildDataChannel get correct window object"); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - info("An answer is received."); - }); - - frameScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - info("Data transport channel is initialized."); - }); - - frameScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - info("Data notification is enabled for data transport channel."); - }); - - var connectionFromEvent; - request.onconnectionavailable = function(aEvent) { - request.onconnectionavailable = null; - connectionFromEvent = aEvent.connection; - ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); - - if (connection) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - }; - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - if (connectionFromEvent) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - frameScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - frameScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -function testReconnectAConnectedConnection() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnectAConnectedConnection ---'); - ok(connection.state, "connected", "Make sure the state is connected."); - - request.reconnect(connection.id).then( - function(aConnection) { - ok(aConnection, "Connection should be available."); - is(aConnection.id, connection.id, "Connection ID should be the same."); - is(aConnection.state, "connected", "The state should be connected."); - is(aConnection, connection, "The connection should be the same."); - - aResolve(); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testReconnectInvalidID() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnectInvalidID ---'); - - request.reconnect("dummyID").then( - function(aConnection) { - ok(false, "Unexpected success."); - teardown(); - aReject(); - }, - function(aError) { - is(aError.name, "NotFoundError", "Should get NotFoundError."); - aResolve(); - } - ); - }); -} - -function testReconnectInvalidURL() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnectInvalidURL ---'); - - var request1 = new PresentationRequest("http://invalidURL"); - request1.reconnect(connection.id).then( - function(aConnection) { - ok(false, "Unexpected success."); - teardown(); - aReject(); - }, - function(aError) { - is(aError.name, "NotFoundError", "Should get NotFoundError."); - aResolve(); - } - ); - }); -} - -function testReconnectIframeConnectedConnection() { - info('--- testReconnectIframeConnectedConnection ---'); - gScript.sendAsyncMessage('save-control-channel-listener'); - return Promise.all([ - new Promise(function(aResolve, aReject) { - commandHandler["connection-connected"] = function(command) { - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - var request1 = new PresentationRequest("http://example1.com"); - request1.reconnect(command.id).then( - function(aConnection) { - is(aConnection.state, "connecting", "The state should be connecting."); - aConnection.onclose = function() { - delete commandHandler["connection-connected"]; - gScript.sendAsyncMessage('restore-control-channel-listener'); - aResolve(); - }; - aConnection.close(); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }; - iframe.contentWindow.postMessage("startConnection", "*"); - }), - new Promise(function(aResolve, aReject) { - commandHandler["notify-connection-closed"] = function(command) { - delete commandHandler["notify-connection-closed"]; - aResolve(); - }; - }), - ]); -} - -function testReconnectIframeClosedConnection() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnectIframeClosedConnection ---'); - gScript.sendAsyncMessage('save-control-channel-listener'); - commandHandler["connection-closed"] = function(command) { - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - var request1 = new PresentationRequest("http://example1.com"); - request1.reconnect(command.id).then( - function(aConnection) { - aConnection.onconnect = function() { - aConnection.onconnect = null; - is(aConnection.state, "connected", "The connection should be connected."); - aConnection.onclose = function() { - aConnection.onclose = null; - ok(true, "The connection is closed."); - delete commandHandler["connection-closed"]; - aResolve(); - }; - aConnection.close(); - gScript.sendAsyncMessage('restore-control-channel-listener'); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }; - iframe.contentWindow.postMessage("closeConnection", "*"); - }); -} - -function testReconnect() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnect ---'); - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - is(url, "http://example.com/", "URLs should be the same."); - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - request.reconnect(connection.id).then( - function(aConnection) { - ok(aConnection, "Connection should be available."); - ok(aConnection.id, "Connection ID should be set."); - is(aConnection.state, "connecting", "The initial state should be connecting."); - is(aConnection, connection, "The reconnected connection should be the same."); - - aConnection.onconnect = function() { - aConnection.onconnect = null; - is(aConnection.state, "connected", "Connection should be connected."); - - const incomingMessage = "test incoming message"; - aConnection.addEventListener('message', function messageHandler(aEvent) { - aConnection.removeEventListener('message', messageHandler); - is(aEvent.data, incomingMessage, "An incoming message should be received."); - aResolve(); - }); - - frameScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - info('teardown-complete'); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnection). - then(testReconnectInvalidID). - then(testReconnectInvalidURL). - then(testReconnectAConnectedConnection). - then(testReconnectIframeConnectedConnection). - then(testReconnectIframeClosedConnection). - then(testCloseConnection). - then(testReconnect). - then(testCloseConnection). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", true]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html b/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html deleted file mode 100644 index dc17209c5..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_sandboxed_presentation.html +++ /dev/null @@ -1,75 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test default request for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268758">Test allow-presentation sandboxing flag</a> -<iframe sandbox="allow-popups allow-scripts allow-same-origin" id="iframe" src="file_presentation_sandboxed_presentation.html"></iframe> -<script type="application/javascript;version=1.8"> - -"use strict"; - -var iframe = document.getElementById("iframe"); -var readyToStart = false; -var testSetuped = false; -function setup() { - return new Promise(function(aResolve, aReject) { - addEventListener("message", function listener(event) { - var message = event.data; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - if (command === "ready-to-start") { - readyToStart = true; - startTest(); - } - } else if (/^DONE$/.exec(message)) { - window.removeEventListener('message', listener); - SimpleTest.finish(); - } - }, false); - - testSetuped = true; - aResolve(); - }); -} - -iframe.onload = startTest(); - -function startTest() { - if (!(testSetuped && readyToStart)) { - return; - } - iframe.contentWindow.postMessage("start", "*"); -} - -function runTests() { - ok(navigator.presentation, "navigator.presentation should be available."); - setup().then(startTest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: "presentation-device-manage", allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ "set": [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", false], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html b/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html deleted file mode 100644 index d0c8af0ad..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_sender_on_terminate_request.html +++ /dev/null @@ -1,187 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test onTerminateRequest at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1276378">Test onTerminateRequest at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var request; -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("http://example.com"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - var connectionFromEvent; - request.onconnectionavailable = function(aEvent) { - request.onconnectionavailable = null; - connectionFromEvent = aEvent.connection; - ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); - - if (connection) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - }; - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - if (connectionFromEvent) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testOnTerminateRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onterminate = function() { - connection.onterminate = null; - is(connection.state, "terminated", "Connection should be closed."); - aResolve(); - }; - - gScript.sendAsyncMessage('trigger-incoming-terminate-request'); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnection). - then(testOnTerminateRequest). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", false], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html deleted file mode 100644 index 27b17bb32..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html +++ /dev/null @@ -1,173 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test startWithDevice for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1239242">Test startWithDevice for B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var request; -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("https://example.com"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnectionWithDevice() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - ok(false, "Device prompt should not be triggered."); - teardown(); - aReject(); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - var connectionFromEvent; - request.onconnectionavailable = function(aEvent) { - request.onconnectionavailable = null; - connectionFromEvent = aEvent.connection; - ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); - - if (connection) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - }; - - request.startWithDevice('id').then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - if (connectionFromEvent) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnectionWithDeviceNotFoundError() { - return new Promise(function(aResolve, aReject) { - request.startWithDevice('').then( - function(aConnection) { - ok(false, "Should not establish connection to an unknown device"); - teardown(); - aReject(); - }, - function(aError) { - is(aError.name, 'NotFoundError', "Expect NotFoundError occurred when establishing a connection"); - aResolve(); - } - ); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnectionWithDevice). - then(testStartConnectionWithDeviceNotFoundError). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.test.enabled", true], - ["dom.presentation.test.stage", 0]]}, - runTests); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html deleted file mode 100644 index f26184f0b..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html +++ /dev/null @@ -1,137 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationConnection API at receiver side</title> - <script type="application/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=1069230">Test for B2G PresentationConnection API at receiver side</a> -<p id="display"></p> -<div id="content" style="display: none"></div> -<pre id="test"></pre> -<script type="application/javascript"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - var iframe = document.createElement('iframe'); - iframe.setAttribute('mozbrowser', 'true'); - iframe.setAttribute('mozpresentation', receiverUrl); - iframe.setAttribute('src', receiverUrl); - - // This event is triggered when the iframe calls "postMessage". - iframe.addEventListener('mozbrowsershowmodalprompt', function listener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - iframe.removeEventListener('mozbrowsershowmodalprompt', listener); - - teardown(); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(iframe); - - aResolve(iframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - gScript.addMessageListener('offer-received', function offerReceivedHandler() { - gScript.removeMessageListener('offer-received', offerReceivedHandler); - info("An offer is received."); - }); - - gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { - gScript.removeMessageListener('answer-sent', answerSentHandler); - ok(aIsValid, "A valid answer is sent."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); - }); - - aResolve(); - }); -} - -function testIncomingSessionRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - - ok(navigator.presentation, "navigator.presentation should be available in in-process pages."); - is(navigator.presentation.receiver, null, "Non-receiving in-process pages shouldn't get a presentation receiver instance."); - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", false], - ["dom.presentation.receiver.enabled", true], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html deleted file mode 100644 index 0935aaaf9..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html +++ /dev/null @@ -1,110 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for connection establishing errors of B2G Presentation API at receiver side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at receiver side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver_establish_connection_error.html'); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - var iframe = document.createElement('iframe'); - iframe.setAttribute('src', receiverUrl); - iframe.setAttribute("mozbrowser", "true"); - iframe.setAttribute("mozpresentation", receiverUrl); - - // This event is triggered when the iframe calls "alert". - iframe.addEventListener("mozbrowsershowmodalprompt", function receiverListener(evt) { - var message = evt.detail.message; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, "")); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, "")); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, "")); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, "")); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - iframe.removeEventListener("mozbrowsershowmodalprompt", - receiverListener); - teardown(); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(iframe); - - aResolve(iframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, 0x80004004 /* NS_ERROR_ABORT */, "The control channel is closed abnormally."); - }); - - aResolve(); - }); -} - -function testIncomingSessionRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: "browser", allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html deleted file mode 100644 index 1dc002644..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html +++ /dev/null @@ -1,81 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for connection establishing timeout of B2G Presentation API at receiver side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing timeout of B2G Presentation API at receiver side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - var promise = new Promise(function(aResolve, aReject) { - // In order to trigger timeout, do not resolve the promise. - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - aResolve(); - }); -} - -function testIncomingSessionRequestReceiverLaunchTimeout() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, 0x80530017 /* NS_ERROR_DOM_TIMEOUT_ERR */, "The control channel is closed due to timeout."); - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', 'http://example.com'); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequestReceiverLaunchTimeout). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false], - ["presentation.receiver.loading.timeout", 10]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js deleted file mode 100644 index d73f84cf8..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_unknown_content_type.test'); - -var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] - .getService(SpecialPowers.Ci.nsIObserverService); - -var receiverIframe; - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('mozbrowser', 'true'); - receiverIframe.setAttribute('mozpresentation', receiverUrl); - receiverIframe.setAttribute('src', receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - receiverIframe.setAttribute("remote", oop); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - - aResolve(receiverIframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - aResolve(); - }); -} - -function testIncomingSessionRequestReceiverLaunchUnknownContentType() { - let promise = Promise.all([ - new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info('Trying to launch receiver page.'); - - receiverIframe.addEventListener('mozbrowserclose', function() { - ok(true, 'observe receiver window closed'); - aResolve(); - }); - }); - }), - new Promise(function(aResolve, aReject) { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, 0x80530020 /* NS_ERROR_DOM_OPERATION_ERR */, 'The control channel is closed due to load failure.'); - aResolve(); - }); - }) - ]); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - return promise; -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequestReceiverLaunchUnknownContentType). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ['dom.presentation.session_transport.data_channel.enable', false], - ['dom.mozBrowserFramesEnabled', true], - ["network.disable.ipc.security", true], - ['dom.ipc.tabs.disabled', false]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html deleted file mode 100644 index 8ade1d72d..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_inproc.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for unknown content type of B2G Presentation API at receiver side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side</a> - <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js"> - </script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html deleted file mode 100644 index b2d2d3c6e..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_unknown_content_type_oop.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for unknown content type of B2G Presentation API at receiver side (OOP)</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287717">Test for unknown content type of B2G Presentation API at receiver side (OOP)</a> - <script type="application/javascript;version=1.8" src="test_presentation_tcp_receiver_establish_connection_unknown_content_type.js"> - </script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html deleted file mode 100644 index bfbc7947a..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html +++ /dev/null @@ -1,178 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G PresentationConnection API at receiver side (OOP)</title> - <script type="application/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=1069230">Test B2G PresentationConnection API at receiver side (OOP)</a> -<p id="display"></p> -<div id="content" style="display: none"></div> -<pre id="test"></pre> -<script type="application/javascript"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_receiver.html'); -var nonReceiverUrl = SimpleTest.getTestFileURL('file_presentation_non_receiver.html'); - -var isReceiverFinished = false; -var isNonReceiverFinished = false; - -var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"] - .getService(SpecialPowers.Ci.nsIObserverService); - -function setup() { - return new Promise(function(aResolve, aReject) { - gScript.sendAsyncMessage('trigger-device-add'); - - // Create a receiver OOP iframe. - var receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('remote', 'true'); - receiverIframe.setAttribute('mozbrowser', 'true'); - receiverIframe.setAttribute('mozpresentation', receiverUrl); - receiverIframe.setAttribute('src', receiverUrl); - - // This event is triggered when the iframe calls "alert". - receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - receiverIframe.removeEventListener('mozbrowsershowmodalprompt', receiverListener); - - isReceiverFinished = true; - - if (isNonReceiverFinished) { - teardown(); - } - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - - aResolve(receiverIframe); - }); - obs.notifyObservers(promise, 'setup-request-promise', null); - - // Create a non-receiver OOP iframe. - var nonReceiverIframe = document.createElement('iframe'); - nonReceiverIframe.setAttribute('remote', 'true'); - nonReceiverIframe.setAttribute('mozbrowser', 'true'); - nonReceiverIframe.setAttribute('src', nonReceiverUrl); - - // This event is triggered when the iframe calls "alert". - nonReceiverIframe.addEventListener('mozbrowsershowmodalprompt', function nonReceiverListener(aEvent) { - var message = aEvent.detail.message; - if (/^OK /.exec(message)) { - ok(true, "Message from iframe: " + message); - } else if (/^KO /.exec(message)) { - ok(false, "Message from iframe: " + message); - } else if (/^INFO /.exec(message)) { - info("Message from iframe: " + message); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, "Messaging from iframe complete."); - nonReceiverIframe.removeEventListener('mozbrowsershowmodalprompt', nonReceiverListener); - - isNonReceiverFinished = true; - - if (isReceiverFinished) { - teardown(); - } - } - }, false); - - document.body.appendChild(nonReceiverIframe); - - gScript.addMessageListener('offer-received', function offerReceivedHandler() { - gScript.removeMessageListener('offer-received', offerReceivedHandler); - info("An offer is received."); - }); - - gScript.addMessageListener('answer-sent', function answerSentHandler(aIsValid) { - gScript.removeMessageListener('answer-sent', answerSentHandler); - ok(aIsValid, "A valid answer is sent."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally."); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - is(aReason, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally."); - }); - - aResolve(); - }); -} - -function testIncomingSessionRequest() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('receiver-launching', function launchReceiverHandler(aSessionId) { - gScript.removeMessageListener('receiver-launching', launchReceiverHandler); - info("Trying to launch receiver page."); - - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-session-request', receiverUrl); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup(). - then(testIncomingSessionRequest); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", false], - ["dom.presentation.receiver.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false], - ["dom.mozBrowserFramesEnabled", true], - ["network.disable.ipc.security", true], - ["dom.ipc.browser_frames.oop_by_default", true]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html deleted file mode 100644 index 8df34c884..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html +++ /dev/null @@ -1,260 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var request; -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("https://example.com"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - var connectionFromEvent; - request.onconnectionavailable = function(aEvent) { - request.onconnectionavailable = null; - connectionFromEvent = aEvent.connection; - ok(connectionFromEvent, "|connectionavailable| event is fired with a connection."); - - if (connection) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - }; - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - - if (connectionFromEvent) { - is(connection, connectionFromEvent, "The connection from promise and the one from |connectionavailable| event should be the same."); - } - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testSend() { - return new Promise(function(aResolve, aReject) { - const outgoingMessage = "test outgoing message"; - - gScript.addMessageListener('message-sent', function messageSentHandler(aMessage) { - gScript.removeMessageListener('message-sent', messageSentHandler); - is(aMessage, outgoingMessage, "The message is sent out."); - aResolve(); - }); - - connection.send(outgoingMessage); - }); -} - -function testIncomingMessage() { - return new Promise(function(aResolve, aReject) { - const incomingMessage = "test incoming message"; - - connection.addEventListener('message', function messageHandler(aEvent) { - connection.removeEventListener('message', messageHandler); - is(aEvent.data, incomingMessage, "An incoming message should be received."); - aResolve(); - }); - - gScript.sendAsyncMessage('trigger-incoming-message', incomingMessage); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -function testReconnect() { - return new Promise(function(aResolve, aReject) { - info('--- testReconnect ---'); - gScript.addMessageListener('control-channel-established', function controlChannelEstablished() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablished); - gScript.sendAsyncMessage("trigger-control-channel-open"); - }); - - gScript.addMessageListener('start-reconnect', function startReconnectHandler(url) { - gScript.removeMessageListener('start-reconnect', startReconnectHandler); - is(url, "https://example.com/", "URLs should be the same.") - gScript.sendAsyncMessage('trigger-reconnected-acked', url); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler() { - gScript.removeMessageListener('offer-sent', offerSentHandler); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - request.reconnect(connection.id).then( - function(aConnection) { - ok(aConnection, "Connection should be available."); - ok(aConnection.id, "Connection ID should be set."); - is(aConnection.state, "connecting", "The initial state should be connecting."); - is(aConnection, connection, "The reconnected connection should be the same."); - - aConnection.onconnect = function() { - aConnection.onconnect = null; - is(aConnection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnection). - then(testSend). - then(testIncomingMessage). - then(testCloseConnection). - then(testReconnect). - then(testCloseConnection). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html deleted file mode 100644 index 60247ec98..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html +++ /dev/null @@ -1,151 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test default request for B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test default request for B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - navigator.presentation.defaultRequest = new PresentationRequest("https://example.com"); - - navigator.presentation.defaultRequest.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler() { - gScript.removeMessageListener('offer-sent', offerSentHandler); - info("An offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - is(navigator.presentation.receiver, undefined, "Sender shouldn't get a presentation receiver instance."); - - navigator.presentation.defaultRequest.onconnectionavailable = function(aEvent) { - navigator.presentation.defaultRequest.onconnectionavailable = null; - connection = aEvent.connection; - ok(connection, "|connectionavailable| event is fired with a connection."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }; - - // Simulate the UA triggers |start()| of the default request. - navigator.presentation.defaultRequest.start(); - }); -} - -function testCloseConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - connection.close(); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - ok(navigator.presentation, "navigator.presentation should be available."); - - testSetup(). - then(testStartConnection). - then(testCloseConnection). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", false], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html deleted file mode 100644 index a95da104f..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html +++ /dev/null @@ -1,160 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for disconnection of B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for disconnection of B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var request; -var connection; - -function testSetup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("http://example.com"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler(aReason) { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - }); - - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - }); - - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-answer'); - }); - - gScript.addMessageListener('answer-received', function answerReceivedHandler() { - gScript.removeMessageListener('answer-received', answerReceivedHandler); - info("An answer is received."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - }); - - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - }); - - gScript.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() { - gScript.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler); - info("Data notification is enabled for data transport channel."); - }); - - request.start().then( - function(aConnection) { - connection = aConnection; - ok(connection, "Connection should be available."); - ok(connection.id, "Connection ID should be set."); - is(connection.state, "connecting", "The initial state should be connecting."); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, "connected", "Connection should be connected."); - aResolve(); - }; - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testDisconnection() { - return new Promise(function(aResolve, aReject) { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - }); - - connection.onclose = function() { - connection.onclose = null; - is(connection.state, "closed", "Connection should be closed."); - aResolve(); - }; - - gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_FAILURE); - }); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - testSetup(). - then(testStartConnection). - then(testDisconnection). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html deleted file mode 100644 index 557ae71a6..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html +++ /dev/null @@ -1,514 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> -<head> - <meta charset="utf-8"> - <title>Test for connection establishing errors of B2G Presentation API at sender side</title> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069230">Test for connection establishing errors of B2G Presentation API at sender side</a> -<script type="application/javascript;version=1.8"> - -'use strict'; - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript.js')); -var request; - -function setup() { - return new Promise(function(aResolve, aReject) { - request = new PresentationRequest("http://example.com"); - - request.getAvailability().then( - function(aAvailability) { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, "Device should be available."); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }, - function(aError) { - ok(false, "Error occurred when getting availability: " + aError); - teardown(); - aReject(); - } - ); - }); -} - -function testStartConnectionCancelPrompt() { - info('--- testStartConnectionCancelPrompt ---'); - return Promise.all([ - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR); - resolve(); - }); - }), - request.start().then( - function(aConnection) { - ok(false, "|start| shouldn't succeed in this case."); - }, - function(aError) { - is(aError.name, "NotAllowedError", "NotAllowedError is expected when the prompt is canceled."); - } - ), - ]); -} - -function testStartConnectionNoDevice() { - info('--- testStartConnectionNoDevice ---'); - return Promise.all([ - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_FOUND_ERR); - resolve(); - }); - }), - request.start().then( - function(aConnection) { - ok(false, "|start| shouldn't succeed in this case."); - }, - function(aError) { - is(aError.name, "NotFoundError", "NotFoundError is expected when no available device."); - } - ), - ]); -} - -function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit() { - info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit ---'); - return Promise.all([ - - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - is(aReason, SpecialPowers.Cr.NS_ERROR_FAILURE, "The control channel is closed with NS_ERROR_FAILURE"); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_FAILURE); - resolve(); - }); - }), - - request.start().then( - function(aConnection) { - is(aConnection.state, "connecting", "The initial state should be connecting."); - return new Promise((resolve) => { - aConnection.onclose = function() { - aConnection.onclose = null; - is(aConnection.state, "closed", "Connection should be closed."); - resolve(); - }; - }); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - } - ), - - ]); -} - -function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit() { - info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit ---'); - return Promise.all([ - - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK"); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK); - resolve(); - }); - }), - - request.start().then( - function(aConnection) { - is(aConnection.state, "connecting", "The initial state should be connecting."); - return new Promise((resolve) => { - aConnection.onclose = function() { - aConnection.onclose = null; - is(aConnection.state, "closed", "Connection should be closed."); - resolve(); - }; - }); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - } - ), - - ]); -} - -function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady() { - info('--- testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady ---'); - return Promise.all([ - - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - is(aReason, SpecialPowers.Cr.NS_ERROR_ABORT, "The control channel is closed with NS_ERROR_ABORT"); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_ERROR_ABORT); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - resolve(); - }); - }), - - request.start().then( - function(aConnection) { - is(aConnection.state, "connecting", "The initial state should be connecting."); - return new Promise((resolve) => { - aConnection.onclose = function() { - aConnection.onclose = null; - is(aConnection.state, "closed", "Connection should be closed."); - resolve(); - }; - }); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - } - ), - - ]); -} - -function testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady() { - info('--- testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady -- '); - return Promise.all([ - - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed with NS_OK"); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-control-channel-close', SpecialPowers.Cr.NS_OK); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - resolve(); - }); - }), - - request.start().then( - function(aConnection) { - is(aConnection.state, "connecting", "The initial state should be connecting."); - return new Promise((resolve) => { - aConnection.onclose = function() { - aConnection.onclose = null; - is(aConnection.state, "closed", "Connection should be closed."); - resolve(); - }; - }); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - } - ), - - ]); -} - -function testStartConnectionUnexpectedDataTransportClose() { - info('--- testStartConnectionUnexpectedDataTransportClose ---'); - return Promise.all([ - - new Promise((resolve) => { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - gScript.removeMessageListener('device-prompt', devicePromptHandler); - info("Device prompt is triggered."); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', controlChannelEstablishedHandler); - info("A control channel is established."); - gScript.sendAsyncMessage('trigger-control-channel-open'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-opened', function controlChannelOpenedHandler() { - gScript.removeMessageListener('control-channel-opened', controlChannelOpenedHandler); - info("The control channel is opened."); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) { - gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler); - info("The control channel is closed. " + aReason); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('offer-sent', function offerSentHandler(aIsValid) { - gScript.removeMessageListener('offer-sent', offerSentHandler); - ok(aIsValid, "A valid offer is sent out."); - info("recv offer-sent."); - gScript.sendAsyncMessage('trigger-incoming-transport'); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-initialized', function dataTransportInitializedHandler() { - gScript.removeMessageListener('data-transport-initialized', dataTransportInitializedHandler); - info("Data transport channel is initialized."); - gScript.sendAsyncMessage('trigger-data-transport-close', SpecialPowers.Cr.NS_ERROR_UNEXPECTED); - resolve(); - }); - }), - - new Promise((resolve) => { - gScript.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) { - gScript.removeMessageListener('data-transport-closed', dataTransportClosedHandler); - info("The data transport is closed. " + aReason); - resolve(); - }); - }), - - request.start().then( - function(aConnection) { - is(aConnection.state, "connecting", "The initial state should be connecting."); - return new Promise((resolve) => { - aConnection.onclose = function() { - aConnection.onclose = null; - is(aConnection.state, "closed", "Connection should be closed."); - resolve(); - }; - }); - }, - function(aError) { - ok(false, "Error occurred when establishing a connection: " + aError); - teardown(); - } - ), - - ]); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - ok(window.PresentationRequest, "PresentationRequest should be available."); - - setup(). - then(testStartConnectionCancelPrompt). - then(testStartConnectionNoDevice). - then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit). - then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit). - then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady). - then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady). - then(testStartConnectionUnexpectedDataTransportClose). - then(teardown); -} - -SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, -], function() { - SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.session_transport.data_channel.enable", false]]}, - runTests); -}); - -</script> -</body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate.js b/dom/presentation/tests/mochitest/test_presentation_terminate.js deleted file mode 100644 index 8ebfd9d64..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate.js +++ /dev/null @@ -1,243 +0,0 @@ -'use strict'; - -SimpleTest.waitForExplicitFinish(); -SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); - -function debug(str) { - // info(str); -} - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_terminate.html'); -var request; -var connection; -var receiverIframe; - -function setup() { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - debug('Got message: device-prompt'); - gScript.removeMessageListener('device-prompt', devicePromptHandler); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { - debug('Got message: sender-launch'); - gScript.removeMessageListener('sender-launch', senderLaunchHandler); - is(url, receiverUrl, 'Receiver: should receive the same url'); - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('mozbrowser', 'true'); - receiverIframe.setAttribute('mozpresentation', receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - receiverIframe.setAttribute('remote', oop); - - receiverIframe.setAttribute('src', receiverUrl); - receiverIframe.addEventListener('mozbrowserloadend', function mozbrowserloadendHander() { - receiverIframe.removeEventListener('mozbrowserloadend', mozbrowserloadendHander); - info('Receiver loaded.'); - }); - - // This event is triggered when the iframe calls 'alert'. - receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(evt) { - var message = evt.detail.message; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, '')); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, '')); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, '')); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, 'Messaging from iframe complete.'); - receiverIframe.removeEventListener('mozbrowsershowmodalprompt', - receiverListener); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - aResolve(receiverIframe); - }); - - var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] - .getService(SpecialPowers.Ci.nsIObserverService); - obs.notifyObservers(promise, 'setup-request-promise', null); - }); - - gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { - debug('Got message: promise-setup-ready'); - gScript.removeMessageListener('promise-setup-ready', - promiseSetupReadyHandler); - gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); - }); - - return Promise.resolve(); -} - -function testCreateRequest() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testCreateRequest ---'); - request = new PresentationRequest(receiverUrl); - request.getAvailability().then((aAvailability) => { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, 'Sender: Device should be available.'); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }).catch((aError) => { - ok(false, 'Sender: Error occurred when getting availability: ' + aError); - teardown(); - aReject(); - }); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - request.start().then((aConnection) => { - connection = aConnection; - ok(connection, 'Sender: Connection should be available.'); - ok(connection.id, 'Sender: Connection ID should be set.'); - is(connection.state, 'connecting', 'Sender: The initial state should be connecting.'); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, 'connected', 'Connection should be connected.'); - aResolve(); - }; - - info('Sender: test terminate at connecting state'); - connection.onterminate = function() { - connection.onterminate = null; - ok(false, 'Should not be able to terminate at connecting state'); - aReject(); - } - connection.terminate(); - }).catch((aError) => { - ok(false, 'Sender: Error occurred when establishing a connection: ' + aError); - teardown(); - aReject(); - }); - }); -} - -function testConnectionTerminate() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testConnectionTerminate---'); - connection.onterminate = function() { - connection.onterminate = null; - is(connection.state, 'terminated', 'Sender: Connection should be terminated.'); - }; - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - gScript.addMessageListener('sender-terminate', function senderTerminateHandler() { - gScript.removeMessageListener('sender-terminate', - senderTerminateHandler); - - Promise.all([ - new Promise((resolve) => { - gScript.addMessageListener('device-disconnected', function deviceDisconnectedHandler() { - gScript.removeMessageListener('device-disconnected', deviceDisconnectedHandler); - ok(true, 'observe device disconnect'); - resolve(); - }); - }), - new Promise((resolve) => { - receiverIframe.addEventListener('mozbrowserclose', function() { - ok(true, 'observe receiver page closing'); - resolve(); - }); - }), - ]).then(aResolve); - - gScript.sendAsyncMessage('trigger-on-terminate-request'); - }); - gScript.addMessageListener('ready-to-terminate', function onReadyToTerminate() { - gScript.removeMessageListener('ready-to-terminate', onReadyToTerminate); - connection.terminate(); - - // test unexpected close right after terminate - connection.onclose = function() { - ok(false, 'close after terminate should do nothing'); - }; - connection.close(); - }); - }); -} - -function testSendAfterTerminate() { - return new Promise(function(aResolve, aReject) { - try { - connection.send('something'); - ok(false, 'PresentationConnection.send should be failed'); - } catch (e) { - is(e.name, 'InvalidStateError', 'Must throw InvalidStateError'); - } - aResolve(); - }); -} - -function testCloseAfterTerminate() { - return Promise.race([ - new Promise(function(aResolve, aReject) { - connection.onclose = function() { - connection.onclose = null; - ok(false, 'close at terminated state should do nothing'); - aResolve(); - }; - connection.close(); - }), - new Promise(function(aResolve, aReject) { - setTimeout(function() { - is(connection.state, 'terminated', 'Sender: Connection should be terminated.'); - aResolve(); - }, 3000); - }), - ]); -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - debug('Got message: teardown-complete'); - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup().then(testCreateRequest) - .then(testStartConnection) - .then(testConnectionTerminate) - .then(testSendAfterTerminate) - .then(testCloseAfterTerminate) - .then(teardown); -} - -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], () => { - SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ['dom.presentation.test.enabled', true], - ['dom.mozBrowserFramesEnabled', true], - ["network.disable.ipc.security", true], - ['dom.ipc.tabs.disabled', false], - ['dom.presentation.test.stage', 0]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js deleted file mode 100644 index a1d477aab..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error.js +++ /dev/null @@ -1,197 +0,0 @@ -'use strict'; - -SimpleTest.waitForExplicitFinish(); -SimpleTest.requestFlakyTimeout('Test for guarantee not firing async event'); - -function debug(str) { - // info(str); -} - -var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('PresentationSessionChromeScript1UA.js')); -var receiverUrl = SimpleTest.getTestFileURL('file_presentation_terminate_establish_connection_error.html'); -var request; -var connection; -var receiverIframe; - -function postMessageToIframe(aType) { - receiverIframe.src = receiverUrl + "#" + - encodeURIComponent(JSON.stringify({ type: aType })); -} - -function setup() { - gScript.addMessageListener('device-prompt', function devicePromptHandler() { - debug('Got message: device-prompt'); - gScript.removeMessageListener('device-prompt', devicePromptHandler); - gScript.sendAsyncMessage('trigger-device-prompt-select'); - }); - - gScript.addMessageListener('control-channel-established', function controlChannelEstablishedHandler() { - gScript.removeMessageListener('control-channel-established', - controlChannelEstablishedHandler); - gScript.sendAsyncMessage('trigger-control-channel-open'); - }); - - gScript.addMessageListener('sender-launch', function senderLaunchHandler(url) { - debug('Got message: sender-launch'); - gScript.removeMessageListener('sender-launch', senderLaunchHandler); - is(url, receiverUrl, 'Receiver: should receive the same url'); - receiverIframe = document.createElement('iframe'); - receiverIframe.setAttribute('mozbrowser', 'true'); - receiverIframe.setAttribute('mozpresentation', receiverUrl); - var oop = location.pathname.indexOf('_inproc') == -1; - receiverIframe.setAttribute('remote', oop); - - receiverIframe.setAttribute('src', receiverUrl); - receiverIframe.addEventListener('mozbrowserloadend', function mozbrowserloadendHander() { - receiverIframe.removeEventListener('mozbrowserloadend', mozbrowserloadendHander); - info('Receiver loaded.'); - }); - - // This event is triggered when the iframe calls 'alert'. - receiverIframe.addEventListener('mozbrowsershowmodalprompt', function receiverListener(evt) { - var message = evt.detail.message; - if (/^OK /.exec(message)) { - ok(true, message.replace(/^OK /, '')); - } else if (/^KO /.exec(message)) { - ok(false, message.replace(/^KO /, '')); - } else if (/^INFO /.exec(message)) { - info(message.replace(/^INFO /, '')); - } else if (/^COMMAND /.exec(message)) { - var command = JSON.parse(message.replace(/^COMMAND /, '')); - gScript.sendAsyncMessage(command.name, command.data); - } else if (/^DONE$/.exec(message)) { - ok(true, 'Messaging from iframe complete.'); - receiverIframe.removeEventListener('mozbrowsershowmodalprompt', - receiverListener); - } - }, false); - - var promise = new Promise(function(aResolve, aReject) { - document.body.appendChild(receiverIframe); - aResolve(receiverIframe); - }); - - var obs = SpecialPowers.Cc['@mozilla.org/observer-service;1'] - .getService(SpecialPowers.Ci.nsIObserverService); - obs.notifyObservers(promise, 'setup-request-promise', null); - }); - - gScript.addMessageListener('promise-setup-ready', function promiseSetupReadyHandler() { - debug('Got message: promise-setup-ready'); - gScript.removeMessageListener('promise-setup-ready', - promiseSetupReadyHandler); - gScript.sendAsyncMessage('trigger-on-session-request', receiverUrl); - }); - - return Promise.resolve(); -} - -function testCreateRequest() { - return new Promise(function(aResolve, aReject) { - info('Sender: --- testCreateRequest ---'); - request = new PresentationRequest(receiverUrl); - request.getAvailability().then((aAvailability) => { - is(aAvailability.value, false, "Sender: should have no available device after setup"); - aAvailability.onchange = function() { - aAvailability.onchange = null; - ok(aAvailability.value, 'Sender: Device should be available.'); - aResolve(); - } - - gScript.sendAsyncMessage('trigger-device-add'); - }).catch((aError) => { - ok(false, 'Sender: Error occurred when getting availability: ' + aError); - teardown(); - aReject(); - }); - }); -} - -function testStartConnection() { - return new Promise(function(aResolve, aReject) { - request.start().then((aConnection) => { - connection = aConnection; - ok(connection, 'Sender: Connection should be available.'); - ok(connection.id, 'Sender: Connection ID should be set.'); - is(connection.state, 'connecting', 'Sender: The initial state should be connecting.'); - connection.onconnect = function() { - connection.onconnect = null; - is(connection.state, 'connected', 'Connection should be connected.'); - aResolve(); - }; - }).catch((aError) => { - ok(false, 'Sender: Error occurred when establishing a connection: ' + aError); - teardown(); - aReject(); - }); - }); -} - -function testConnectionTerminate() { - info('Sender: --- testConnectionTerminate---'); - let promise = Promise.all([ - new Promise(function(aResolve, aReject) { - connection.onclose = function() { - connection.onclose = null; - is(connection.state, 'closed', 'Sender: Connection should be closed.'); - aResolve(); - }; - }), - new Promise(function(aResolve, aReject) { - function deviceDisconnectedHandler() { - gScript.removeMessageListener('device-disconnected', deviceDisconnectedHandler); - ok(true, 'should not receive device disconnect'); - aResolve(); - } - - gScript.addMessageListener('device-disconnected', deviceDisconnectedHandler); - }), - new Promise(function(aResolve, aReject) { - receiverIframe.addEventListener('mozbrowserclose', function() { - ok(true, 'observe receiver page closing'); - aResolve(); - }); - }) - ]); - - gScript.addMessageListener('prepare-for-terminate', function prepareForTerminateHandler() { - debug('Got message: prepare-for-terminate'); - gScript.removeMessageListener('prepare-for-terminate', prepareForTerminateHandler); - gScript.sendAsyncMessage('trigger-control-channel-error'); - postMessageToIframe('ready-to-terminate'); - }); - - return promise; -} - -function teardown() { - gScript.addMessageListener('teardown-complete', function teardownCompleteHandler() { - debug('Got message: teardown-complete'); - gScript.removeMessageListener('teardown-complete', teardownCompleteHandler); - gScript.destroy(); - SimpleTest.finish(); - }); - gScript.sendAsyncMessage('teardown'); -} - -function runTests() { - setup().then(testCreateRequest) - .then(testStartConnection) - .then(testConnectionTerminate) - .then(teardown); -} - -SpecialPowers.pushPermissions([ - {type: 'presentation-device-manage', allow: false, context: document}, - {type: 'browser', allow: true, context: document}, -], () => { - SpecialPowers.pushPrefEnv({ 'set': [['dom.presentation.enabled', true], - ["dom.presentation.controller.enabled", true], - ["dom.presentation.receiver.enabled", true], - ['dom.presentation.test.enabled', true], - ['dom.mozBrowserFramesEnabled', true], - ["network.disable.ipc.security", true], - ['dom.ipc.tabs.disabled', false], - ['dom.presentation.test.stage', 0]]}, - runTests); -}); diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html deleted file mode 100644 index ccf0767f1..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_inproc.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset='utf-8'> - <title>Test for control channel establish error during PresentationConnection.terminate()</title> - <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> - <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> - </head> - <body> - <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1289292'> - Test for constrol channel establish error during PresentationConnection.terminate()</a> - <script type='application/javascript;version=1.8' src='test_presentation_terminate_establish_connection_error.js'> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html b/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html deleted file mode 100644 index ccf0767f1..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate_establish_connection_error_oop.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset='utf-8'> - <title>Test for control channel establish error during PresentationConnection.terminate()</title> - <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> - <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> - </head> - <body> - <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1289292'> - Test for constrol channel establish error during PresentationConnection.terminate()</a> - <script type='application/javascript;version=1.8' src='test_presentation_terminate_establish_connection_error.js'> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html b/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html deleted file mode 100644 index 33bbcda57..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate_inproc.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset='utf-8'> - <title>Test for PresentationConnection.terminate()</title> - <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> - <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> - </head> - <body> - <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1276378'> - Test for PresentationConnection.terminate()</a> - <script type='application/javascript;version=1.8' src='test_presentation_terminate.js'> - </script> - </body> -</html> diff --git a/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html b/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html deleted file mode 100644 index 33bbcda57..000000000 --- a/dom/presentation/tests/mochitest/test_presentation_terminate_oop.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<!-- vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: --> -<html> - <!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - <head> - <meta charset='utf-8'> - <title>Test for PresentationConnection.terminate()</title> - <link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css'/> - <script type='application/javascript' src='/tests/SimpleTest/SimpleTest.js'></script> - </head> - <body> - <a target='_blank' href='https://bugzilla.mozilla.org/show_bug.cgi?id=1276378'> - Test for PresentationConnection.terminate()</a> - <script type='application/javascript;version=1.8' src='test_presentation_terminate.js'> - </script> - </body> -</html> diff --git a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js deleted file mode 100644 index 137a5609a..000000000 --- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js +++ /dev/null @@ -1,1318 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* global Services, do_register_cleanup, do_test_pending */ - -"use strict"; - -const { classes: Cc, interfaces: Ci, manager: Cm, results: Cr, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -const INFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1"; -const PROVIDER_CONTRACT_ID = "@mozilla.org/presentation-device/multicastdns-provider;1"; -const SD_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1"; -const UUID_CONTRACT_ID = "@mozilla.org/uuid-generator;1"; -const SERVER_CONTRACT_ID = "@mozilla.org/presentation/control-service;1"; - -const PREF_DISCOVERY = "dom.presentation.discovery.enabled"; -const PREF_DISCOVERABLE = "dom.presentation.discoverable"; -const PREF_DEVICENAME= "dom.presentation.device.name"; - -const LATEST_VERSION = 1; -const SERVICE_TYPE = "_presentation-ctrl._tcp"; -const versionAttr = Cc["@mozilla.org/hash-property-bag;1"] - .createInstance(Ci.nsIWritablePropertyBag2); -versionAttr.setPropertyAsUint32("version", LATEST_VERSION); - -var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - -function sleep(aMs) { - let deferred = Promise.defer(); - - let timer = Cc["@mozilla.org/timer;1"] - .createInstance(Ci.nsITimer); - - timer.initWithCallback({ - notify: function () { - deferred.resolve(); - }, - }, aMs, timer.TYPE_ONE_SHOT); - - return deferred.promise; -} - -function MockFactory(aClass) { - this._cls = aClass; -} -MockFactory.prototype = { - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Cr.NS_ERROR_NO_AGGREGATION; - } - switch(typeof(this._cls)) { - case "function": - return new this._cls().QueryInterface(aIID); - case "object": - return this._cls.QueryInterface(aIID); - default: - return null; - } - }, - lockFactory: function(aLock) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) -}; - -function ContractHook(aContractID, aClass) { - this._contractID = aContractID; - this.classID = Cc[UUID_CONTRACT_ID].getService(Ci.nsIUUIDGenerator).generateUUID(); - this._newFactory = new MockFactory(aClass); - - if (!this.hookedMap.has(this._contractID)) { - this.hookedMap.set(this._contractID, []); - } - - this.init(); -} - -ContractHook.prototype = { - hookedMap: new Map(), // remember only the most original factory. - - init: function() { - this.reset(); - - let oldContract = this.unregister(); - this.hookedMap.get(this._contractID).push(oldContract); - registrar.registerFactory(this.classID, - "", - this._contractID, - this._newFactory); - - do_register_cleanup(() => { this.cleanup.apply(this); }); - }, - - reset: function() {}, - - cleanup: function() { - this.reset(); - - this.unregister(); - let prevContract = this.hookedMap.get(this._contractID).pop(); - - if (prevContract.factory) { - registrar.registerFactory(prevContract.classID, - "", - this._contractID, - prevContract.factory); - } - }, - - unregister: function() { - var classID, factory; - - try { - classID = registrar.contractIDToCID(this._contractID); - factory = Cm.getClassObject(Cc[this._contractID], Ci.nsIFactory); - } catch (ex) { - classID = ""; - factory = null; - } - - if (factory) { - registrar.unregisterFactory(classID, factory); - } - - return { classID: classID, factory: factory }; - } -}; - -function MockDNSServiceInfo() {} -MockDNSServiceInfo.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceInfo]), - - set host(aHost) { - this._host = aHost; - }, - - get host() { - return this._host; - }, - - set address(aAddress) { - this._address = aAddress; - }, - - get address() { - return this._address; - }, - - set port(aPort) { - this._port = aPort; - }, - - get port() { - return this._port; - }, - - set serviceName(aServiceName) { - this._serviceName = aServiceName; - }, - - get serviceName() { - return this._serviceName; - }, - - set serviceType(aServiceType) { - this._serviceType = aServiceType; - }, - - get serviceType() { - return this._serviceType; - }, - - set domainName(aDomainName) { - this._domainName = aDomainName; - }, - - get domainName() { - return this._domainName; - }, - - set attributes(aAttributes) { - this._attributes = aAttributes; - }, - - get attributes() { - return this._attributes; - } -}; - -function TestPresentationDeviceListener() { - this.devices = {}; -} -TestPresentationDeviceListener.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - - addDevice: function(device) { this.devices[device.id] = device; }, - removeDevice: function(device) { delete this.devices[device.id]; }, - updateDevice: function(device) { this.devices[device.id] = device; }, - onSessionRequest: function(device, url, presentationId, controlChannel) {}, - - count: function() { - var size = 0, key; - for (key in this.devices) { - if (this.devices.hasOwnProperty(key)) { - ++size; - } - } - return size; - } -}; - -function createDevice(host, port, serviceName, serviceType, domainName, attributes) { - let device = new MockDNSServiceInfo(); - device.host = host || ""; - device.port = port || 0; - device.address = host || ""; - device.serviceName = serviceName || ""; - device.serviceType = serviceType || ""; - device.domainName = domainName || ""; - device.attributes = attributes || versionAttr; - return device; -} - -function registerService() { - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let deferred = Promise.defer(); - - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) {}, - registerService: function(serviceInfo, listener) { - deferred.resolve(); - this.serviceRegistered++; - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() { - this.serviceUnregistered++; - }.bind(this) - }; - }, - resolveService: function(serviceInfo, listener) {}, - serviceRegistered: 0, - serviceUnregistered: 0 - }; - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - - Assert.equal(mockObj.serviceRegistered, 0); - Assert.equal(mockObj.serviceUnregistered, 0); - - // Register - provider.listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - }; - - deferred.promise.then(function() { - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 0); - - // Unregister - provider.listener = null; - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 1); - - run_next_test(); - }); -} - -function noRegisterService() { - Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); - - let deferred = Promise.defer(); - - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) {}, - registerService: function(serviceInfo, listener) { - deferred.resolve(); - Assert.ok(false, "should not register service if not discoverable"); - }, - resolveService: function(serviceInfo, listener) {}, - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - - // Try register - provider.listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - }; - - let race = Promise.race([ - deferred.promise, - sleep(1000), - ]); - - race.then(() => { - provider.listener = null; - - run_next_test(); - }); -} - -function registerServiceDynamically() { - Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); - - let deferred = Promise.defer(); - - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) {}, - registerService: function(serviceInfo, listener) { - deferred.resolve(); - this.serviceRegistered++; - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() { - this.serviceUnregistered++; - }.bind(this) - }; - }, - resolveService: function(serviceInfo, listener) {}, - serviceRegistered: 0, - serviceUnregistered: 0 - }; - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - - Assert.equal(mockObj.serviceRegistered, 0); - Assert.equal(mockObj.serviceRegistered, 0); - - // Try Register - provider.listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - }; - - Assert.equal(mockObj.serviceRegistered, 0); - Assert.equal(mockObj.serviceUnregistered, 0); - - // Enable registration - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - deferred.promise.then(function() { - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 0); - - // Disable registration - Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 1); - - // Try unregister - provider.listener = null; - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 1); - - run_next_test(); - }); -} - -function addDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = new TestPresentationDeviceListener(); - Assert.equal(listener.count(), 0); - - // Start discovery - provider.listener = listener; - Assert.equal(listener.count(), 1); - - // Force discovery again - provider.forceDiscovery(); - Assert.equal(listener.count(), 1); - - provider.listener = null; - Assert.equal(listener.count(), 1); - - run_next_test(); -} - -function filterDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) { - let tests = [ - { requestedUrl: "app://fling-player.gaiamobile.org/index.html", supported: true }, - { requestedUrl: "app://notification-receiver.gaiamobile.org/index.html", supported: true }, - { requestedUrl: "http://example.com", supported: true }, - { requestedUrl: "https://example.com", supported: true }, - { requestedUrl: "ftp://example.com", supported: false }, - { requestedUrl: "app://unknown-app-id", supported: false }, - { requestedUrl: "unknowSchem://example.com", supported: false }, - ]; - - for (let test of tests) { - Assert.equal(device.isRequestedUrlSupported(test.requestedUrl), test.supported); - } - - provider.listener = null; - run_next_test(); - }, - updateDevice: function() {}, - removeDevice: function() {}, - onSessionRequest: function() {}, - }; - - provider.listener = listener; -} - -function handleSessionRequest() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, false); - - const testUrl = "http://example.com"; - const testPresentationId = "test-presentation-id"; - const testDeviceName = "test-device-name"; - - Services.prefs.setCharPref(PREF_DEVICENAME, testDeviceName); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - connect: function(deviceInfo) { - this.request = { - deviceInfo: deviceInfo, - }; - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - }; - }, - id: "", - version: LATEST_VERSION, - isCompatibleServer: function(version) { - return this.version === version; - } - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) { - this.device = device; - }, - }; - - provider.listener = listener; - - let controlChannel = listener.device.establishControlChannel(); - - Assert.equal(mockServerObj.request.deviceInfo.id, mockDevice.host); - Assert.equal(mockServerObj.request.deviceInfo.address, mockDevice.host); - Assert.equal(mockServerObj.request.deviceInfo.port, mockDevice.port); - Assert.equal(mockServerObj.id, testDeviceName); - - provider.listener = null; - - run_next_test(); -} - -function handleOnSessionRequest() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - startServer: function() {}, - sessionRequest: function() {}, - close: function() {}, - id: '', - version: LATEST_VERSION, - port: 0, - listener: null, - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - onSessionRequest: function(device, url, presentationId, controlChannel) { - Assert.ok(true, "receive onSessionRequest event"); - this.request = { - deviceId: device.id, - url: url, - presentationId: presentationId, - }; - } - }; - - provider.listener = listener; - - const deviceInfo = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), - id: mockDevice.host, - address: mockDevice.host, - port: 54321, - }; - - const testUrl = "http://example.com"; - const testPresentationId = "test-presentation-id"; - const testControlChannel = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - }; - provider.QueryInterface(Ci.nsIPresentationControlServerListener) - .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel); - - Assert.equal(listener.request.deviceId, deviceInfo.id); - Assert.equal(listener.request.url, testUrl); - Assert.equal(listener.request.presentationId, testPresentationId); - - provider.listener = null; - - run_next_test(); -} - -function handleOnSessionRequestFromUnknownDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) {}, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) {} - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - startServer: function() {}, - sessionRequest: function() {}, - close: function() {}, - id: '', - version: LATEST_VERSION, - port: 0, - listener: null, - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) { - Assert.ok(false, "shouldn't create any new device"); - }, - removeDevice: function(device) { - Assert.ok(false, "shouldn't remote any device"); - }, - updateDevice: function(device) { - Assert.ok(false, "shouldn't update any device"); - }, - onSessionRequest: function(device, url, presentationId, controlChannel) { - Assert.ok(true, "receive onSessionRequest event"); - this.request = { - deviceId: device.id, - url: url, - presentationId: presentationId, - }; - } - }; - - provider.listener = listener; - - const deviceInfo = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), - id: "unknown-device.local", - address: "unknown-device.local", - port: 12345, - }; - - const testUrl = "http://example.com"; - const testPresentationId = "test-presentation-id"; - const testControlChannel = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - }; - provider.QueryInterface(Ci.nsIPresentationControlServerListener) - .onSessionRequest(deviceInfo, testUrl, testPresentationId, testControlChannel); - - Assert.equal(listener.request.deviceId, deviceInfo.id); - Assert.equal(listener.request.url, testUrl); - Assert.equal(listener.request.presentationId, testPresentationId); - - provider.listener = null; - - run_next_test(); -} - -function noAddDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - - let mockDevice = createDevice("device.local", 12345, "service.name", SERVICE_TYPE); - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - Assert.ok(false, "shouldn't perform any device discovery"); - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - } - }; - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - }; - provider.listener = listener; - provider.forceDiscovery(); - provider.listener = null; - - run_next_test(); -} - -function ignoreIncompatibleDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - - let deferred = Promise.defer(); - - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) { - deferred.resolve(); - listener.onServiceRegistered(createDevice("", - 54321, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - startServer: function() { - Services.tm.currentThread.dispatch(() => { - this.listener.onServerReady(this.port, this.certFingerprint); - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - sessionRequest: function() {}, - close: function() {}, - id: '', - version: LATEST_VERSION, - isCompatibleServer: function(version) { - return false; - }, - port: 54321, - certFingerprint: 'mock-cert-fingerprint', - listener: null, - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = new TestPresentationDeviceListener(); - - // Register service - provider.listener = listener; - - deferred.promise.then(function() { - Assert.equal(mockServerObj.id, mockDevice.host); - - // Start discovery - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - Assert.equal(listener.count(), 0); - - provider.listener = null; - - run_next_test(); - }); -} - -function ignoreSelfDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - - let deferred = Promise.defer(); - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) { - deferred.resolve(); - listener.onServiceRegistered(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - startServer: function() { - Services.tm.currentThread.dispatch(() => { - this.listener.onServerReady(this.port, this.certFingerprint); - }, Ci.nsIThread.DISPATCH_NORMAL); - }, - sessionRequest: function() {}, - close: function() {}, - id: '', - version: LATEST_VERSION, - isCompatibleServer: function(version) { - return this.version === version; - }, - port: 54321, - certFingerprint: 'mock-cert-fingerprint', - listener: null, - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = new TestPresentationDeviceListener(); - - // Register service - provider.listener = listener; - deferred.promise.then(() => { - Assert.equal(mockServerObj.id, mockDevice.host); - - // Start discovery - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - Assert.equal(listener.count(), 0); - - provider.listener = null; - - run_next_test(); - }); -} - -function addDeviceDynamically() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - } - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = new TestPresentationDeviceListener(); - provider.listener = listener; - Assert.equal(listener.count(), 0); - - // Enable discovery - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - Assert.equal(listener.count(), 1); - - // Try discovery again - provider.forceDiscovery(); - Assert.equal(listener.count(), 1); - - // Try discovery once more - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - provider.forceDiscovery(); - Assert.equal(listener.count(), 1); - - provider.listener = null; - - run_next_test(); -} - -function updateDevice() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE); - let mockDevice2 = createDevice("A.local", 23456, "N2", SERVICE_TYPE); - - let mockObj = { - discovered: false, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - - if (!this.discovered) { - listener.onServiceFound(mockDevice1); - } else { - listener.onServiceFound(mockDevice2); - } - this.discovered = true; - - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() { - listener.onDiscoveryStopped(serviceType); - } - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceType, SERVICE_TYPE); - if (serviceInfo.serviceName == "N1") { - listener.onServiceResolved(mockDevice1); - } else if (serviceInfo.serviceName == "N2") { - listener.onServiceResolved(mockDevice2); - } else { - Assert.ok(false); - } - } - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - - addDevice: function(device) { - Assert.ok(!this.isDeviceAdded); - Assert.equal(device.id, mockDevice1.host); - Assert.equal(device.name, mockDevice1.serviceName); - this.isDeviceAdded = true; - }, - removeDevice: function(device) { Assert.ok(false); }, - updateDevice: function(device) { - Assert.ok(!this.isDeviceUpdated); - Assert.equal(device.id, mockDevice2.host); - Assert.equal(device.name, mockDevice2.serviceName); - this.isDeviceUpdated = true; - }, - - isDeviceAdded: false, - isDeviceUpdated: false - }; - Assert.equal(listener.isDeviceAdded, false); - Assert.equal(listener.isDeviceUpdated, false); - - // Start discovery - provider.listener = listener; // discover: N1 - - Assert.equal(listener.isDeviceAdded, true); - Assert.equal(listener.isDeviceUpdated, false); - - // temporarily disable to stop discovery and re-enable - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - provider.forceDiscovery(); // discover: N2 - - Assert.equal(listener.isDeviceAdded, true); - Assert.equal(listener.isDeviceUpdated, true); - - provider.listener = null; - - run_next_test(); -} - -function diffDiscovery() { - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - let mockDevice1 = createDevice("A.local", 12345, "N1", SERVICE_TYPE); - let mockDevice2 = createDevice("B.local", 23456, "N2", SERVICE_TYPE); - let mockDevice3 = createDevice("C.local", 45678, "N3", SERVICE_TYPE); - - let mockObj = { - discovered: false, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - - if (!this.discovered) { - listener.onServiceFound(mockDevice1); - listener.onServiceFound(mockDevice2); - } else { - listener.onServiceFound(mockDevice1); - listener.onServiceFound(mockDevice3); - } - this.discovered = true; - - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() { - listener.onDiscoveryStopped(serviceType); - } - }; - }, - registerService: function(serviceInfo, listener) {}, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceType, SERVICE_TYPE); - if (serviceInfo.serviceName == "N1") { - listener.onServiceResolved(mockDevice1); - } else if (serviceInfo.serviceName == "N2") { - listener.onServiceResolved(mockDevice2); - } else if (serviceInfo.serviceName == "N3") { - listener.onServiceResolved(mockDevice3); - } else { - Assert.ok(false); - } - } - }; - - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = new TestPresentationDeviceListener(); - Assert.equal(listener.count(), 0); - - // Start discovery - provider.listener = listener; // discover: N1, N2 - Assert.equal(listener.count(), 2); - Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); - Assert.equal(listener.devices['B.local'].name, mockDevice2.serviceName); - Assert.ok(!listener.devices['C.local']); - - // temporarily disable to stop discovery and re-enable - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - provider.forceDiscovery(); // discover: N1, N3, going to remove: N2 - Assert.equal(listener.count(), 3); - Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); - Assert.equal(listener.devices['B.local'].name, mockDevice2.serviceName); - Assert.equal(listener.devices['C.local'].name, mockDevice3.serviceName); - - // temporarily disable to stop discovery and re-enable - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - provider.forceDiscovery(); // discover: N1, N3, remove: N2 - Assert.equal(listener.count(), 2); - Assert.equal(listener.devices['A.local'].name, mockDevice1.serviceName); - Assert.ok(!listener.devices['B.local']); - Assert.equal(listener.devices['C.local'].name, mockDevice3.serviceName); - - provider.listener = null; - - run_next_test(); -} - -function serverClosed() { - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - Services.prefs.setBoolPref(PREF_DISCOVERY, true); - - let mockDevice = createDevice("device.local", - 12345, - "service.name", - SERVICE_TYPE); - - let mockObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) { - listener.onDiscoveryStarted(serviceType); - listener.onServiceFound(createDevice("", - 0, - mockDevice.serviceName, - mockDevice.serviceType)); - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() {} - }; - }, - registerService: function(serviceInfo, listener) { - this.serviceRegistered++; - return { - QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]), - cancel: function() { - this.serviceUnregistered++; - }.bind(this) - }; - }, - resolveService: function(serviceInfo, listener) { - Assert.equal(serviceInfo.serviceName, mockDevice.serviceName); - Assert.equal(serviceInfo.serviceType, mockDevice.serviceType); - listener.onServiceResolved(createDevice(mockDevice.host, - mockDevice.port, - mockDevice.serviceName, - mockDevice.serviceType)); - }, - serviceRegistered: 0, - serviceUnregistered: 0 - }; - let contractHook = new ContractHook(SD_CONTRACT_ID, mockObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - - Assert.equal(mockObj.serviceRegistered, 0); - Assert.equal(mockObj.serviceUnregistered, 0); - - // Register - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) { this.devices.push(device); }, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - devices: [] - }; - Assert.equal(listener.devices.length, 0); - - provider.listener = listener; - Assert.equal(mockObj.serviceRegistered, 1); - Assert.equal(mockObj.serviceUnregistered, 0); - Assert.equal(listener.devices.length, 1); - - let serverListener = provider.QueryInterface(Ci.nsIPresentationControlServerListener); - let randomPort = 9527; - serverListener.onServerReady(randomPort, ''); - - Assert.equal(mockObj.serviceRegistered, 2); - Assert.equal(mockObj.serviceUnregistered, 1); - Assert.equal(listener.devices.length, 1); - - // Unregister - provider.listener = null; - Assert.equal(mockObj.serviceRegistered, 2); - Assert.equal(mockObj.serviceUnregistered, 2); - Assert.equal(listener.devices.length, 1); - - run_next_test(); -} - -function serverRetry() { - Services.prefs.setBoolPref(PREF_DISCOVERY, false); - Services.prefs.setBoolPref(PREF_DISCOVERABLE, true); - - let isRetrying = false; - - let mockSDObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]), - startDiscovery: function(serviceType, listener) {}, - registerService: function(serviceInfo, listener) { - Assert.ok(isRetrying, "register service after retrying startServer"); - provider.listener = null; - run_next_test(); - }, - resolveService: function(serviceInfo, listener) {} - }; - - let mockServerObj = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlService]), - startServer: function(encrypted, port) { - if (!isRetrying) { - isRetrying = true; - Services.tm.currentThread.dispatch(() => { - this.listener.onServerStopped(Cr.NS_ERROR_FAILURE); - }, Ci.nsIThread.DISPATCH_NORMAL); - } else { - this.port = 54321; - Services.tm.currentThread.dispatch(() => { - this.listener.onServerReady(this.port, this.certFingerprint); - }, Ci.nsIThread.DISPATCH_NORMAL); - } - }, - sessionRequest: function() {}, - close: function() {}, - id: '', - version: LATEST_VERSION, - port: 0, - certFingerprint: 'mock-cert-fingerprint', - listener: null, - }; - - let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj); - let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj); - let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider); - let listener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceListener, - Ci.nsISupportsWeakReference]), - addDevice: function(device) {}, - removeDevice: function(device) {}, - updateDevice: function(device) {}, - onSessionRequest: function(device, url, presentationId, controlChannel) {} - }; - - provider.listener = listener; -} - -function run_test() { - // Need profile dir to store the key / cert - do_get_profile(); - // Ensure PSM is initialized - Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); - - let infoHook = new ContractHook(INFO_CONTRACT_ID, MockDNSServiceInfo); - - do_register_cleanup(() => { - Services.prefs.clearUserPref(PREF_DISCOVERY); - Services.prefs.clearUserPref(PREF_DISCOVERABLE); - }); - - add_test(registerService); - add_test(noRegisterService); - add_test(registerServiceDynamically); - add_test(addDevice); - add_test(filterDevice); - add_test(handleSessionRequest); - add_test(handleOnSessionRequest); - add_test(handleOnSessionRequestFromUnknownDevice); - add_test(noAddDevice); - add_test(ignoreIncompatibleDevice); - add_test(ignoreSelfDevice); - add_test(addDeviceDynamically); - add_test(updateDevice); - add_test(diffDiscovery); - add_test(serverClosed); - add_test(serverRetry); - - run_next_test(); -} diff --git a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js b/dom/presentation/tests/xpcshell/test_presentation_device_manager.js deleted file mode 100644 index 68f07df65..000000000 --- a/dom/presentation/tests/xpcshell/test_presentation_device_manager.js +++ /dev/null @@ -1,244 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -'use strict'; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -const manager = Cc['@mozilla.org/presentation-device/manager;1'] - .getService(Ci.nsIPresentationDeviceManager); - -function TestPresentationDevice() {} - - -function TestPresentationControlChannel() {} - -TestPresentationControlChannel.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannel]), - sendOffer: function(offer) {}, - sendAnswer: function(answer) {}, - disconnect: function() {}, - launch: function() {}, - terminate: function() {}, - reconnect: function() {}, - set listener(listener) {}, - get listener() {}, -}; - -var testProvider = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDeviceProvider]), - - forceDiscovery: function() { - }, - set listener(listener) { - }, - get listener() { - }, -}; - -const forbiddenRequestedUrl = 'http://example.com'; -var testDevice = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevice]), - id: 'id', - name: 'name', - type: 'type', - establishControlChannel: function(url, presentationId) { - return null; - }, - disconnect: function() {}, - isRequestedUrlSupported: function(requestedUrl) { - return forbiddenRequestedUrl !== requestedUrl; - }, -}; - -function addProvider() { - Object.defineProperty(testProvider, 'listener', { - configurable: true, - set: function(listener) { - Assert.strictEqual(listener, manager, 'listener setter is invoked by PresentationDeviceManager'); - delete testProvider.listener; - run_next_test(); - }, - }); - manager.addDeviceProvider(testProvider); -} - -function forceDiscovery() { - testProvider.forceDiscovery = function() { - testProvider.forceDiscovery = function() {}; - Assert.ok(true, 'forceDiscovery is invoked by PresentationDeviceManager'); - run_next_test(); - }; - manager.forceDiscovery(); -} - -function addDevice() { - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let updatedDevice = subject.QueryInterface(Ci.nsIPresentationDevice); - Assert.equal(updatedDevice.id, testDevice.id, 'expected device id'); - Assert.equal(updatedDevice.name, testDevice.name, 'expected device name'); - Assert.equal(updatedDevice.type, testDevice.type, 'expected device type'); - Assert.equal(data, 'add', 'expected update type'); - - Assert.ok(manager.deviceAvailable, 'device is available'); - - let devices = manager.getAvailableDevices(); - Assert.equal(devices.length, 1, 'expect 1 available device'); - - let device = devices.queryElementAt(0, Ci.nsIPresentationDevice); - Assert.equal(device.id, testDevice.id, 'expected device id'); - Assert.equal(device.name, testDevice.name, 'expected device name'); - Assert.equal(device.type, testDevice.type, 'expected device type'); - - run_next_test(); - }, 'presentation-device-change', false); - manager.QueryInterface(Ci.nsIPresentationDeviceListener).addDevice(testDevice); -} - -function updateDevice() { - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let updatedDevice = subject.QueryInterface(Ci.nsIPresentationDevice); - Assert.equal(updatedDevice.id, testDevice.id, 'expected device id'); - Assert.equal(updatedDevice.name, testDevice.name, 'expected device name'); - Assert.equal(updatedDevice.type, testDevice.type, 'expected device type'); - Assert.equal(data, 'update', 'expected update type'); - - Assert.ok(manager.deviceAvailable, 'device is available'); - - let devices = manager.getAvailableDevices(); - Assert.equal(devices.length, 1, 'expect 1 available device'); - - let device = devices.queryElementAt(0, Ci.nsIPresentationDevice); - Assert.equal(device.id, testDevice.id, 'expected device id'); - Assert.equal(device.name, testDevice.name, 'expected name after device update'); - Assert.equal(device.type, testDevice.type, 'expected device type'); - - run_next_test(); - }, 'presentation-device-change', false); - testDevice.name = 'updated-name'; - manager.QueryInterface(Ci.nsIPresentationDeviceListener).updateDevice(testDevice); -} - -function filterDevice() { - let presentationUrls = Cc['@mozilla.org/array;1'].createInstance(Ci.nsIMutableArray); - let url = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString); - url.data = forbiddenRequestedUrl; - presentationUrls.appendElement(url, false); - let devices = manager.getAvailableDevices(presentationUrls); - Assert.equal(devices.length, 0, 'expect 0 available device for example.com'); - run_next_test(); -} - -function sessionRequest() { - let testUrl = 'http://www.example.org/'; - let testPresentationId = 'test-presentation-id'; - let testControlChannel = new TestPresentationControlChannel(); - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let request = subject.QueryInterface(Ci.nsIPresentationSessionRequest); - - Assert.equal(request.device.id, testDevice.id, 'expected device'); - Assert.equal(request.url, testUrl, 'expected requesting URL'); - Assert.equal(request.presentationId, testPresentationId, 'expected presentation Id'); - - run_next_test(); - }, 'presentation-session-request', false); - manager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onSessionRequest(testDevice, testUrl, testPresentationId, testControlChannel); -} - -function terminateRequest() { - let testUrl = 'http://www.example.org/'; - let testPresentationId = 'test-presentation-id'; - let testControlChannel = new TestPresentationControlChannel(); - let testIsFromReceiver = true; - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let request = subject.QueryInterface(Ci.nsIPresentationTerminateRequest); - - Assert.equal(request.device.id, testDevice.id, 'expected device'); - Assert.equal(request.presentationId, testPresentationId, 'expected presentation Id'); - Assert.equal(request.isFromReceiver, testIsFromReceiver, 'expected isFromReceiver'); - - run_next_test(); - }, 'presentation-terminate-request', false); - manager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onTerminateRequest(testDevice, testPresentationId, - testControlChannel, testIsFromReceiver); -} - -function reconnectRequest() { - let testUrl = 'http://www.example.org/'; - let testPresentationId = 'test-presentation-id'; - let testControlChannel = new TestPresentationControlChannel(); - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let request = subject.QueryInterface(Ci.nsIPresentationSessionRequest); - - Assert.equal(request.device.id, testDevice.id, 'expected device'); - Assert.equal(request.url, testUrl, 'expected requesting URL'); - Assert.equal(request.presentationId, testPresentationId, 'expected presentation Id'); - - run_next_test(); - }, 'presentation-reconnect-request', false); - manager.QueryInterface(Ci.nsIPresentationDeviceListener) - .onReconnectRequest(testDevice, testUrl, testPresentationId, testControlChannel); -} - -function removeDevice() { - Services.obs.addObserver(function observer(subject, topic, data) { - Services.obs.removeObserver(observer, topic); - - let updatedDevice = subject.QueryInterface(Ci.nsIPresentationDevice); - Assert.equal(updatedDevice.id, testDevice.id, 'expected device id'); - Assert.equal(updatedDevice.name, testDevice.name, 'expected device name'); - Assert.equal(updatedDevice.type, testDevice.type, 'expected device type'); - Assert.equal(data, 'remove', 'expected update type'); - - Assert.ok(!manager.deviceAvailable, 'device is not available'); - - let devices = manager.getAvailableDevices(); - Assert.equal(devices.length, 0, 'expect 0 available device'); - - run_next_test(); - }, 'presentation-device-change', false); - manager.QueryInterface(Ci.nsIPresentationDeviceListener).removeDevice(testDevice); -} - -function removeProvider() { - Object.defineProperty(testProvider, 'listener', { - configurable: true, - set: function(listener) { - Assert.strictEqual(listener, null, 'unsetListener is invoked by PresentationDeviceManager'); - delete testProvider.listener; - run_next_test(); - }, - }); - manager.removeDeviceProvider(testProvider); -} - -add_test(addProvider); -add_test(forceDiscovery); -add_test(addDevice); -add_test(updateDevice); -add_test(filterDevice); -add_test(sessionRequest); -add_test(terminateRequest); -add_test(reconnectRequest); -add_test(removeDevice); -add_test(removeProvider); - -function run_test() { - run_next_test(); -} diff --git a/dom/presentation/tests/xpcshell/test_presentation_session_transport.js b/dom/presentation/tests/xpcshell/test_presentation_session_transport.js deleted file mode 100644 index 8e207bc22..000000000 --- a/dom/presentation/tests/xpcshell/test_presentation_session_transport.js +++ /dev/null @@ -1,198 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -'use strict'; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC } = Components; -const ServerSocket = CC("@mozilla.org/network/server-socket;1", - "nsIServerSocket", - "init"); - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -var testServer = null; -var clientTransport = null; -var serverTransport = null; - -var clientBuilder = null; -var serverBuilder = null; - -const clientMessage = "Client Message"; -const serverMessage = "Server Message"; - -const address = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); -address.data = "127.0.0.1"; -const addresses = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); -addresses.appendElement(address, false); - -const serverChannelDescription = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), - type: 1, - tcpAddress: addresses, -}; - -var isClientReady = false; -var isServerReady = false; -var isClientClosed = false; -var isServerClosed = false; - -const clientCallback = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), - notifyTransportReady: function () { - Assert.ok(true, "Client transport ready."); - - isClientReady = true; - if (isClientReady && isServerReady) { - run_next_test(); - } - }, - notifyTransportClosed: function (aReason) { - Assert.ok(true, "Client transport is closed."); - - isClientClosed = true; - if (isClientClosed && isServerClosed) { - run_next_test(); - } - }, - notifyData: function(aData) { - Assert.equal(aData, serverMessage, "Client transport receives data."); - run_next_test(); - }, -}; - -const serverCallback = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportCallback]), - notifyTransportReady: function () { - Assert.ok(true, "Server transport ready."); - - isServerReady = true; - if (isClientReady && isServerReady) { - run_next_test(); - } - }, - notifyTransportClosed: function (aReason) { - Assert.ok(true, "Server transport is closed."); - - isServerClosed = true; - if (isClientClosed && isServerClosed) { - run_next_test(); - } - }, - notifyData: function(aData) { - Assert.equal(aData, clientMessage, "Server transport receives data."); - run_next_test(); - }, -}; - -const clientListener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), - onSessionTransport(aTransport) { - Assert.ok(true, "Client Transport is built."); - clientTransport = aTransport; - clientTransport.callback = clientCallback; - - if (serverTransport) { - run_next_test(); - } - } -} - -const serverListener = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]), - onSessionTransport(aTransport) { - Assert.ok(true, "Server Transport is built."); - serverTransport = aTransport; - serverTransport.callback = serverCallback; - serverTransport.enableDataNotification(); - - if (clientTransport) { - run_next_test(); - } - } -} - -function TestServer() { - this.serverSocket = ServerSocket(-1, true, -1); - this.serverSocket.asyncListen(this) -} - -TestServer.prototype = { - onSocketAccepted: function(aSocket, aTransport) { - print("Test server gets a client connection."); - serverBuilder = Cc["@mozilla.org/presentation/presentationtcpsessiontransport;1"] - .createInstance(Ci.nsIPresentationTCPSessionTransportBuilder); - serverBuilder.buildTCPSenderTransport(aTransport, serverListener); - }, - onStopListening: function(aSocket) { - print("Test server stops listening."); - }, - close: function() { - if (this.serverSocket) { - this.serverSocket.close(); - this.serverSocket = null; - } - } -}; - -// Set up the transport connection and ensure |notifyTransportReady| triggered -// at both sides. -function setup() { - clientBuilder = Cc["@mozilla.org/presentation/presentationtcpsessiontransport;1"] - .createInstance(Ci.nsIPresentationTCPSessionTransportBuilder); - clientBuilder.buildTCPReceiverTransport(serverChannelDescription, clientListener); -} - -// Test |selfAddress| attribute of |nsIPresentationSessionTransport|. -function selfAddress() { - var serverSelfAddress = serverTransport.selfAddress; - Assert.equal(serverSelfAddress.address, address.data, "The self address of server transport should be set."); - Assert.equal(serverSelfAddress.port, testServer.serverSocket.port, "The port of server transport should be set."); - - var clientSelfAddress = clientTransport.selfAddress; - Assert.ok(clientSelfAddress.address, "The self address of client transport should be set."); - Assert.ok(clientSelfAddress.port, "The port of client transport should be set."); - - run_next_test(); -} - -// Test the client sends a message and then a corresponding notification gets -// triggered at the server side. -function clientSendMessage() { - clientTransport.send(clientMessage); -} - -// Test the server sends a message an then a corresponding notification gets -// triggered at the client side. -function serverSendMessage() { - serverTransport.send(serverMessage); - // The client enables data notification even after the incoming message has - // been sent, and should still be able to consume it. - clientTransport.enableDataNotification(); -} - -function transportClose() { - clientTransport.close(Cr.NS_OK); -} - -function shutdown() { - testServer.close(); - run_next_test(); -} - -add_test(setup); -add_test(selfAddress); -add_test(clientSendMessage); -add_test(serverSendMessage); -add_test(transportClose); -add_test(shutdown); - -function run_test() { - testServer = new TestServer(); - // Get the port of the test server. - serverChannelDescription.tcpPort = testServer.serverSocket.port; - - run_next_test(); -} diff --git a/dom/presentation/tests/xpcshell/test_presentation_state_machine.js b/dom/presentation/tests/xpcshell/test_presentation_state_machine.js deleted file mode 100644 index fcaf34da6..000000000 --- a/dom/presentation/tests/xpcshell/test_presentation_state_machine.js +++ /dev/null @@ -1,236 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* jshint esnext:true, globalstrict:true, moz:true, undef:true, unused:true */ -/* globals Components,Assert,run_next_test,add_test,do_execute_soon */ - -'use strict'; - -const { utils: Cu, results: Cr } = Components; - -/* globals ControllerStateMachine */ -Cu.import('resource://gre/modules/presentation/ControllerStateMachine.jsm'); -/* globals ReceiverStateMachine */ -Cu.import('resource://gre/modules/presentation/ReceiverStateMachine.jsm'); -/* globals State */ -Cu.import('resource://gre/modules/presentation/StateMachineHelper.jsm'); - -const testControllerId = 'test-controller-id'; -const testPresentationId = 'test-presentation-id'; -const testUrl = 'http://example.org'; - -let mockControllerChannel = {}; -let mockReceiverChannel = {}; - -let controllerState = new ControllerStateMachine(mockControllerChannel, testControllerId); -let receiverState = new ReceiverStateMachine(mockReceiverChannel); - -mockControllerChannel.sendCommand = function(command) { - do_execute_soon(function() { - receiverState.onCommand(command); - }); -}; - -mockReceiverChannel.sendCommand = function(command) { - do_execute_soon(function() { - controllerState.onCommand(command); - }); -}; - -function connect() { - Assert.equal(controllerState.state, State.INIT, 'controller in init state'); - Assert.equal(receiverState.state, State.INIT, 'receiver in init state'); - // step 1: underlying connection is ready - controllerState.onChannelReady(); - Assert.equal(controllerState.state, State.CONNECTING, 'controller in connecting state'); - receiverState.onChannelReady(); - Assert.equal(receiverState.state, State.CONNECTING, 'receiver in connecting state'); - - // step 2: receiver reply to connect command - mockReceiverChannel.notifyDeviceConnected = function(deviceId) { - Assert.equal(deviceId, testControllerId, 'receiver connect to mock controller'); - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - - // step 3: controller receive connect-ack command - mockControllerChannel.notifyDeviceConnected = function() { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - run_next_test(); - }; - }; -} - -function launch() { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - - controllerState.launch(testPresentationId, testUrl); - mockReceiverChannel.notifyLaunch = function(presentationId, url) { - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received'); - Assert.equal(url, testUrl, 'expected url received'); - - mockControllerChannel.notifyLaunch = function(presentationId) { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack'); - - run_next_test(); - }; - }; -} - -function terminateByController() { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - - controllerState.terminate(testPresentationId); - mockReceiverChannel.notifyTerminate = function(presentationId) { - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received'); - - mockControllerChannel.notifyTerminate = function(presentationId) { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack'); - - run_next_test(); - }; - - receiverState.terminateAck(presentationId); - }; -} - -function terminateByReceiver() { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - - receiverState.terminate(testPresentationId); - mockControllerChannel.notifyTerminate = function(presentationId) { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received'); - - mockReceiverChannel.notifyTerminate = function(presentationId) { - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - Assert.equal(presentationId, testPresentationId, 'expected presentationId received from ack'); - run_next_test(); - }; - - controllerState.terminateAck(presentationId); - }; -} - -function exchangeSDP() { - Assert.equal(controllerState.state, State.CONNECTED, 'controller in connected state'); - Assert.equal(receiverState.state, State.CONNECTED, 'receiver in connected state'); - - const testOffer = 'test-offer'; - const testAnswer = 'test-answer'; - const testIceCandidate = 'test-ice-candidate'; - controllerState.sendOffer(testOffer); - mockReceiverChannel.notifyOffer = function(offer) { - Assert.equal(offer, testOffer, 'expected offer received'); - - receiverState.sendAnswer(testAnswer); - mockControllerChannel.notifyAnswer = function(answer) { - Assert.equal(answer, testAnswer, 'expected answer received'); - - controllerState.updateIceCandidate(testIceCandidate); - mockReceiverChannel.notifyIceCandidate = function(candidate) { - Assert.equal(candidate, testIceCandidate, 'expected ice candidate received in receiver'); - - receiverState.updateIceCandidate(testIceCandidate); - mockControllerChannel.notifyIceCandidate = function(candidate) { - Assert.equal(candidate, testIceCandidate, 'expected ice candidate received in controller'); - - run_next_test(); - }; - }; - }; - }; -} - -function disconnect() { - // step 1: controller send disconnect command - controllerState.onChannelClosed(Cr.NS_OK, false); - Assert.equal(controllerState.state, State.CLOSING, 'controller in closing state'); - - mockReceiverChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, Cr.NS_OK, 'receive close reason'); - Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state'); - - receiverState.onChannelClosed(Cr.NS_OK, true); - Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state'); - - mockControllerChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, Cr.NS_OK, 'receive close reason'); - Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state'); - - run_next_test(); - }; - controllerState.onChannelClosed(Cr.NS_OK, true); - }; -} - -function receiverDisconnect() { - // initial state: controller and receiver are connected - controllerState.state = State.CONNECTED; - receiverState.state = State.CONNECTED; - - // step 1: controller send disconnect command - receiverState.onChannelClosed(Cr.NS_OK, false); - Assert.equal(receiverState.state, State.CLOSING, 'receiver in closing state'); - - mockControllerChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, Cr.NS_OK, 'receive close reason'); - Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state'); - - controllerState.onChannelClosed(Cr.NS_OK, true); - Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state'); - - mockReceiverChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, Cr.NS_OK, 'receive close reason'); - Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state'); - - run_next_test(); - }; - receiverState.onChannelClosed(Cr.NS_OK, true); - }; -} - -function abnormalDisconnect() { - // initial state: controller and receiver are connected - controllerState.state = State.CONNECTED; - receiverState.state = State.CONNECTED; - - const testErrorReason = Cr.NS_ERROR_FAILURE; - // step 1: controller send disconnect command - controllerState.onChannelClosed(testErrorReason, false); - Assert.equal(controllerState.state, State.CLOSING, 'controller in closing state'); - - mockReceiverChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, testErrorReason, 'receive abnormal close reason'); - Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state'); - - receiverState.onChannelClosed(Cr.NS_OK, true); - Assert.equal(receiverState.state, State.CLOSED, 'receiver in closed state'); - - mockControllerChannel.notifyDisconnected = function(reason) { - Assert.equal(reason, testErrorReason, 'receive abnormal close reason'); - Assert.equal(controllerState.state, State.CLOSED, 'controller in closed state'); - - run_next_test(); - }; - controllerState.onChannelClosed(Cr.NS_OK, true); - }; -} - -add_test(connect); -add_test(launch); -add_test(terminateByController); -add_test(terminateByReceiver); -add_test(exchangeSDP); -add_test(disconnect); -add_test(receiverDisconnect); -add_test(abnormalDisconnect); - -function run_test() { // jshint ignore:line - run_next_test(); -} diff --git a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js b/dom/presentation/tests/xpcshell/test_tcp_control_channel.js deleted file mode 100644 index 5f3df584d..000000000 --- a/dom/presentation/tests/xpcshell/test_tcp_control_channel.js +++ /dev/null @@ -1,398 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -'use strict'; - -const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); - -var pcs; - -// Call |run_next_test| if all functions in |names| are called -function makeJointSuccess(names) { - let funcs = {}, successCount = 0; - names.forEach(function(name) { - funcs[name] = function() { - do_print('got expected: ' + name); - if (++successCount === names.length) - run_next_test(); - }; - }); - return funcs; -} - -function TestDescription(aType, aTcpAddress, aTcpPort) { - this.type = aType; - this.tcpAddress = Cc["@mozilla.org/array;1"] - .createInstance(Ci.nsIMutableArray); - for (let address of aTcpAddress) { - let wrapper = Cc["@mozilla.org/supports-cstring;1"] - .createInstance(Ci.nsISupportsCString); - wrapper.data = address; - this.tcpAddress.appendElement(wrapper, false); - } - this.tcpPort = aTcpPort; -} - -TestDescription.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationChannelDescription]), -} - -const CONTROLLER_CONTROL_CHANNEL_PORT = 36777; -const PRESENTER_CONTROL_CHANNEL_PORT = 36888; - -var CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_OK; -var candidate; - -// presenter's presentation channel description -const OFFER_ADDRESS = '192.168.123.123'; -const OFFER_PORT = 123; - -// controller's presentation channel description -const ANSWER_ADDRESS = '192.168.321.321'; -const ANSWER_PORT = 321; - -function loopOfferAnser() { - pcs = Cc["@mozilla.org/presentation/control-service;1"] - .createInstance(Ci.nsIPresentationControlService); - pcs.id = 'controllerID'; - pcs.listener = { - onServerReady: function() { - testPresentationServer(); - } - }; - - // First run with TLS enabled. - pcs.startServer(true, PRESENTER_CONTROL_CHANNEL_PORT); -} - - -function testPresentationServer() { - let yayFuncs = makeJointSuccess(['controllerControlChannelClose', - 'presenterControlChannelClose', - 'controllerControlChannelReconnect', - 'presenterControlChannelReconnect']); - let presenterControlChannel; - - pcs.listener = { - - onSessionRequest: function(deviceInfo, url, presentationId, controlChannel) { - presenterControlChannel = controlChannel; - Assert.equal(deviceInfo.id, pcs.id, 'expected device id'); - Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address'); - Assert.equal(url, 'http://example.com', 'expected url'); - Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); - - presenterControlChannel.listener = { - status: 'created', - onOffer: function(aOffer) { - Assert.equal(this.status, 'opened', '1. presenterControlChannel: get offer, send answer'); - this.status = 'onOffer'; - - let offer = aOffer.QueryInterface(Ci.nsIPresentationChannelDescription); - Assert.strictEqual(offer.tcpAddress.queryElementAt(0,Ci.nsISupportsCString).data, - OFFER_ADDRESS, - 'expected offer address array'); - Assert.equal(offer.tcpPort, OFFER_PORT, 'expected offer port'); - try { - let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; - let answer = new TestDescription(tcpType, [ANSWER_ADDRESS], ANSWER_PORT); - presenterControlChannel.sendAnswer(answer); - } catch (e) { - Assert.ok(false, 'sending answer fails' + e); - } - }, - onAnswer: function(aAnswer) { - Assert.ok(false, 'get answer'); - }, - onIceCandidate: function(aCandidate) { - Assert.ok(true, '3. presenterControlChannel: get ice candidate, close channel'); - let recvCandidate = JSON.parse(aCandidate); - for (let key in recvCandidate) { - if (typeof(recvCandidate[key]) !== "function") { - Assert.equal(recvCandidate[key], candidate[key], "key " + key + " should match."); - } - } - presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); - }, - notifyConnected: function() { - Assert.equal(this.status, 'created', '0. presenterControlChannel: opened'); - this.status = 'opened'; - }, - notifyDisconnected: function(aReason) { - Assert.equal(this.status, 'onOffer', '4. presenterControlChannel: closed'); - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'presenterControlChannel notify closed'); - this.status = 'closed'; - yayFuncs.controllerControlChannelClose(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; - }, - onReconnectRequest: function(deviceInfo, url, presentationId, controlChannel) { - Assert.equal(url, 'http://example.com', 'expected url'); - Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); - yayFuncs.presenterControlChannelReconnect(); - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlServerListener]), - }; - - let presenterDeviceInfo = { - id: 'presentatorID', - address: '127.0.0.1', - port: PRESENTER_CONTROL_CHANNEL_PORT, - certFingerprint: pcs.certFingerprint, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), - }; - - let controllerControlChannel = pcs.connect(presenterDeviceInfo); - - controllerControlChannel.listener = { - status: 'created', - onOffer: function(offer) { - Assert.ok(false, 'get offer'); - }, - onAnswer: function(aAnswer) { - Assert.equal(this.status, 'opened', '2. controllerControlChannel: get answer, send ICE candidate'); - - let answer = aAnswer.QueryInterface(Ci.nsIPresentationChannelDescription); - Assert.strictEqual(answer.tcpAddress.queryElementAt(0,Ci.nsISupportsCString).data, - ANSWER_ADDRESS, - 'expected answer address array'); - Assert.equal(answer.tcpPort, ANSWER_PORT, 'expected answer port'); - candidate = { - candidate: "1 1 UDP 1 127.0.0.1 34567 type host", - sdpMid: "helloworld", - sdpMLineIndex: 1 - }; - controllerControlChannel.sendIceCandidate(JSON.stringify(candidate)); - }, - onIceCandidate: function(aCandidate) { - Assert.ok(false, 'get ICE candidate'); - }, - notifyConnected: function() { - Assert.equal(this.status, 'created', '0. controllerControlChannel: opened, send offer'); - controllerControlChannel.launch('testPresentationId', 'http://example.com'); - this.status = 'opened'; - try { - let tcpType = Ci.nsIPresentationChannelDescription.TYPE_TCP; - let offer = new TestDescription(tcpType, [OFFER_ADDRESS], OFFER_PORT) - controllerControlChannel.sendOffer(offer); - } catch (e) { - Assert.ok(false, 'sending offer fails:' + e); - } - }, - notifyDisconnected: function(aReason) { - this.status = 'closed'; - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. controllerControlChannel notify closed'); - yayFuncs.presenterControlChannelClose(); - - let reconnectControllerControlChannel = pcs.connect(presenterDeviceInfo); - reconnectControllerControlChannel.listener = { - notifyConnected: function() { - reconnectControllerControlChannel.reconnect('testPresentationId', 'http://example.com'); - }, - notifyReconnected: function() { - yayFuncs.controllerControlChannelReconnect(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; -} - -function terminateRequest() { - let yayFuncs = makeJointSuccess(['controllerControlChannelConnected', - 'controllerControlChannelDisconnected', - 'presenterControlChannelDisconnected', - 'terminatedByController', - 'terminatedByReceiver']); - let controllerControlChannel; - let terminatePhase = 'controller'; - - pcs.listener = { - onTerminateRequest: function(deviceInfo, presentationId, controlChannel, isFromReceiver) { - Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address'); - Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); - controlChannel.terminate(presentationId); // Reply terminate ack. - - if (terminatePhase === 'controller') { - controllerControlChannel = controlChannel; - Assert.equal(deviceInfo.id, pcs.id, 'expected controller device id'); - Assert.equal(isFromReceiver, false, 'expected request from controller'); - yayFuncs.terminatedByController(); - - controllerControlChannel.listener = { - notifyConnected: function() { - Assert.ok(true, 'control channel notify connected'); - yayFuncs.controllerControlChannelConnected(); - - terminatePhase = 'receiver'; - controllerControlChannel.terminate('testPresentationId'); - }, - notifyDisconnected: function(aReason) { - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, 'controllerControlChannel notify disconncted'); - yayFuncs.controllerControlChannelDisconnected(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; - } else { - Assert.equal(deviceInfo.id, presenterDeviceInfo.id, 'expected presenter device id'); - Assert.equal(isFromReceiver, true, 'expected request from receiver'); - yayFuncs.terminatedByReceiver(); - presenterControlChannel.disconnect(CLOSE_CONTROL_CHANNEL_REASON); - } - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]), - }; - - let presenterDeviceInfo = { - id: 'presentatorID', - address: '127.0.0.1', - port: PRESENTER_CONTROL_CHANNEL_PORT, - certFingerprint: pcs.certFingerprint, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), - }; - - let presenterControlChannel = pcs.connect(presenterDeviceInfo); - - presenterControlChannel.listener = { - notifyConnected: function() { - presenterControlChannel.terminate('testPresentationId'); - }, - notifyDisconnected: function(aReason) { - Assert.equal(aReason, CLOSE_CONTROL_CHANNEL_REASON, '4. presenterControlChannel notify disconnected'); - yayFuncs.presenterControlChannelDisconnected(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; -} - -function terminateRequestAbnormal() { - let yayFuncs = makeJointSuccess(['controllerControlChannelConnected', - 'controllerControlChannelDisconnected', - 'presenterControlChannelDisconnected']); - let controllerControlChannel; - - pcs.listener = { - onTerminateRequest: function(deviceInfo, presentationId, controlChannel, isFromReceiver) { - Assert.equal(deviceInfo.id, pcs.id, 'expected controller device id'); - Assert.equal(deviceInfo.address, '127.0.0.1', 'expected device address'); - Assert.equal(presentationId, 'testPresentationId', 'expected presentation id'); - Assert.equal(isFromReceiver, false, 'expected request from controller'); - controlChannel.terminate('unmatched-presentationId'); // Reply abnormal terminate ack. - - controllerControlChannel = controlChannel; - - controllerControlChannel.listener = { - notifyConnected: function() { - Assert.ok(true, 'control channel notify connected'); - yayFuncs.controllerControlChannelConnected(); - }, - notifyDisconnected: function(aReason) { - Assert.equal(aReason, Cr.NS_ERROR_FAILURE, 'controllerControlChannel notify disconncted with error'); - yayFuncs.controllerControlChannelDisconnected(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServerListener]), - }; - - let presenterDeviceInfo = { - id: 'presentatorID', - address: '127.0.0.1', - port: PRESENTER_CONTROL_CHANNEL_PORT, - certFingerprint: pcs.certFingerprint, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPDeviceInfo]), - }; - - let presenterControlChannel = pcs.connect(presenterDeviceInfo); - - presenterControlChannel.listener = { - notifyConnected: function() { - presenterControlChannel.terminate('testPresentationId'); - }, - notifyDisconnected: function(aReason) { - Assert.equal(aReason, Cr.NS_ERROR_FAILURE, '4. presenterControlChannel notify disconnected with error'); - yayFuncs.presenterControlChannelDisconnected(); - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationControlChannelListener]), - }; -} - -function setOffline() { - pcs.listener = { - onServerReady: function(aPort, aCertFingerprint) { - Assert.notEqual(aPort, 0, 'TCPPresentationServer port changed and the port should be valid'); - pcs.close(); - run_next_test(); - }, - }; - - // Let the server socket restart automatically. - Services.io.offline = true; - Services.io.offline = false; -} - -function oneMoreLoop() { - try { - pcs.listener = { - onServerReady: function() { - testPresentationServer(); - } - }; - - // Second run with TLS disabled. - pcs.startServer(false, PRESENTER_CONTROL_CHANNEL_PORT); - } catch (e) { - Assert.ok(false, 'TCP presentation init fail:' + e); - run_next_test(); - } -} - - -function shutdown() -{ - pcs.listener = { - onServerReady: function(aPort, aCertFingerprint) { - Assert.ok(false, 'TCPPresentationServer port changed'); - }, - }; - pcs.close(); - Assert.equal(pcs.port, 0, "TCPPresentationServer closed"); - run_next_test(); -} - -// Test manually close control channel with NS_ERROR_FAILURE -function changeCloseReason() { - CLOSE_CONTROL_CHANNEL_REASON = Cr.NS_ERROR_FAILURE; - run_next_test(); -} - -add_test(loopOfferAnser); -add_test(terminateRequest); -add_test(terminateRequestAbnormal); -add_test(setOffline); -add_test(changeCloseReason); -add_test(oneMoreLoop); -add_test(shutdown); - -function run_test() { - // Need profile dir to store the key / cert - do_get_profile(); - // Ensure PSM is initialized - Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); - - Services.prefs.setBoolPref("dom.presentation.tcp_server.debug", true); - - do_register_cleanup(() => { - Services.prefs.clearUserPref("dom.presentation.tcp_server.debug"); - }); - - run_next_test(); -} diff --git a/dom/presentation/tests/xpcshell/xpcshell.ini b/dom/presentation/tests/xpcshell/xpcshell.ini deleted file mode 100644 index 8a9c305a0..000000000 --- a/dom/presentation/tests/xpcshell/xpcshell.ini +++ /dev/null @@ -1,9 +0,0 @@ -[DEFAULT] -head = -tail = - -[test_multicast_dns_device_provider.js] -[test_presentation_device_manager.js] -[test_presentation_session_transport.js] -[test_tcp_control_channel.js] -[test_presentation_state_machine.js] diff --git a/dom/push/PushRecord.jsm b/dom/push/PushRecord.jsm index 08a7678e0..58f808e6c 100644 --- a/dom/push/PushRecord.jsm +++ b/dom/push/PushRecord.jsm @@ -9,7 +9,6 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -145,13 +144,14 @@ PushRecord.prototype = { return Date.now(); } - if (AppConstants.MOZ_ANDROID_HISTORY) { - let result = yield Messaging.sendRequestForResult({ - type: "History:GetPrePathLastVisitedTimeMilliseconds", - prePath: this.uri.prePath, - }); - return result == 0 ? -Infinity : result; - } +#ifdef MOZ_ANDROID_HISTORY + let result = yield Messaging.sendRequestForResult({ + type: "History:GetPrePathLastVisitedTimeMilliseconds", + prePath: this.uri.prePath, + }); + + return result == 0 ? -Infinity : result; +#endif // Places History transition types that can fire a // `pushsubscriptionchange` event when the user visits a site with expired push diff --git a/dom/push/PushService.jsm b/dom/push/PushService.jsm index 373807024..07cf70d21 100644 --- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -10,7 +10,6 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Timer.jsm"); @@ -24,14 +23,14 @@ const { const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm"); const CONNECTION_PROTOCOLS = (function() { - if ('android' != AppConstants.MOZ_WIDGET_TOOLKIT) { +#ifdef MOZ_WIDGET_ANDROID + const {PushServiceAndroidGCM} = Cu.import("resource://gre/modules/PushServiceAndroidGCM.jsm"); + return [PushServiceAndroidGCM]; +#else const {PushServiceWebSocket} = Cu.import("resource://gre/modules/PushServiceWebSocket.jsm"); const {PushServiceHttp2} = Cu.import("resource://gre/modules/PushServiceHttp2.jsm"); return [PushServiceWebSocket, PushServiceHttp2]; - } else { - const {PushServiceAndroidGCM} = Cu.import("resource://gre/modules/PushServiceAndroidGCM.jsm"); - return [PushServiceAndroidGCM]; - } +#endif })(); XPCOMUtils.defineLazyServiceGetter(this, "gPushNotifier", diff --git a/dom/push/PushServiceWebSocket.jsm b/dom/push/PushServiceWebSocket.jsm index 46b12b8f0..54348e71c 100644 --- a/dom/push/PushServiceWebSocket.jsm +++ b/dom/push/PushServiceWebSocket.jsm @@ -10,7 +10,6 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/dom/push/moz.build b/dom/push/moz.build index 7eee8896f..d138b270c 100644 --- a/dom/push/moz.build +++ b/dom/push/moz.build @@ -12,12 +12,15 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'PushCrypto.jsm', 'PushDB.jsm', - 'PushRecord.jsm', - 'PushService.jsm', 'PushServiceHttp2.jsm', 'PushServiceWebSocket.jsm', ] +EXTRA_PP_JS_MODULES += [ + 'PushRecord.jsm', + 'PushService.jsm', +] + MOCHITEST_MANIFESTS += [ 'test/mochitest.ini', ] diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp index d07ad7945..6d4f297d6 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -555,7 +555,21 @@ permitsPort(const nsAString& aEnforcementScheme, int32_t resourcePort; nsresult rv = aResourceURI->GetPort(&resourcePort); - NS_ENSURE_SUCCESS(rv, false); + if (NS_FAILED(rv) && aEnforcementPort.IsEmpty()) { + // If we cannot get a Port (e.g. because of an Custom Protocol handler) + // we need to check if a default port is associated with the Scheme + if (aEnforcementScheme.IsEmpty()) { + return false; + } + int defaultPortforScheme = + NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get()); + + // If there is no default port associated with the Scheme ( + // defaultPortforScheme == -1) or it is an externally handled protocol ( + // defaultPortforScheme == 0 ) and the csp does not enforce a port - we can + // allow not having a port + return (defaultPortforScheme == -1 || defaultPortforScheme == 0); + } // Avoid unnecessary string creation/manipulation and don't block the // load if the resource to be loaded uses the default port for that diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp index 86377e75a..ec84d784b 100644 --- a/dom/system/OSFileConstants.cpp +++ b/dom/system/OSFileConstants.cpp @@ -1048,22 +1048,11 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global) } #endif // defined(XP_MACOSX) - // sqlite3 is linked from different places depending on the platform + // sqlite3 is always a shared lib nsAutoString libsqlite3; -#if defined(ANDROID) - // On Android, we use the system's libsqlite3 - libsqlite3.AppendLiteral(DLL_PREFIX); - libsqlite3.AppendLiteral("sqlite3"); - libsqlite3.AppendLiteral(DLL_SUFFIX); -#elif defined(XP_WIN) - // On Windows, for some reason, this is part of nss3.dll libsqlite3.AppendLiteral(DLL_PREFIX); - libsqlite3.AppendLiteral("nss3"); + libsqlite3.AppendLiteral("mozsqlite3"); libsqlite3.AppendLiteral(DLL_SUFFIX); -#else - // On other platforms, we link sqlite3 into libxul - libsqlite3 = libxul; -#endif // defined(ANDROID) || defined(XP_WIN) if (!SetStringProperty(cx, objPath, "libsqlite3", libsqlite3)) { return false; diff --git a/dom/webidl/FlyWebDiscoveryManager.webidl b/dom/webidl/FlyWebDiscoveryManager.webidl deleted file mode 100644 index 963cebf20..000000000 --- a/dom/webidl/FlyWebDiscoveryManager.webidl +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: IDL; 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/. - */ - -dictionary FlyWebDiscoveredService { - DOMString serviceId = ""; - DOMString displayName = ""; - DOMString transport = ""; - DOMString serviceType = ""; - DOMString cert = ""; - DOMString path = ""; -}; - -dictionary FlyWebPairedService { - FlyWebDiscoveredService discoveredService; - DOMString hostname = ""; - DOMString uiUrl = ""; -}; - -callback interface FlyWebPairingCallback { - void pairingSucceeded(optional FlyWebPairedService service); - void pairingFailed(DOMString error); -}; - -callback interface FlyWebDiscoveryCallback { - void onDiscoveredServicesChanged(sequence<FlyWebDiscoveredService> serviceList); -}; - -[ChromeOnly, ChromeConstructor, Exposed=(Window,System)] -interface FlyWebDiscoveryManager { - sequence<FlyWebDiscoveredService> listServices(); - - unsigned long startDiscovery(FlyWebDiscoveryCallback aCallback); - void stopDiscovery(unsigned long aId); - - void pairWithService(DOMString serviceId, FlyWebPairingCallback callback); -}; diff --git a/dom/webidl/FlyWebFetchEvent.webidl b/dom/webidl/FlyWebFetchEvent.webidl deleted file mode 100644 index 4bee424e5..000000000 --- a/dom/webidl/FlyWebFetchEvent.webidl +++ /dev/null @@ -1,13 +0,0 @@ -/* -*- Mode: IDL; 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/. - */ - -[Pref="dom.flyweb.enabled"] -interface FlyWebFetchEvent : Event { - [SameObject] readonly attribute Request request; - - [Throws] - void respondWith(Promise<Response> r); -}; diff --git a/dom/webidl/FlyWebPublish.webidl b/dom/webidl/FlyWebPublish.webidl deleted file mode 100644 index 0c8714a2a..000000000 --- a/dom/webidl/FlyWebPublish.webidl +++ /dev/null @@ -1,23 +0,0 @@ -/* -*- Mode: IDL; 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/. - */ - -[Pref="dom.flyweb.enabled"] -interface FlyWebPublishedServer : EventTarget { - readonly attribute DOMString name; - readonly attribute DOMString? uiUrl; - - void close(); - - attribute EventHandler onclose; - attribute EventHandler onfetch; - attribute EventHandler onwebsocket; -}; - -dictionary FlyWebPublishOptions { - DOMString? uiUrl = null; // URL to user interface. Can be different server. Makes - // endpoint show up in browser's "local services" UI. - // If relative, resolves against the root of the server. -}; diff --git a/dom/webidl/FlyWebWebSocketEvent.webidl b/dom/webidl/FlyWebWebSocketEvent.webidl deleted file mode 100644 index 9a47c6dec..000000000 --- a/dom/webidl/FlyWebWebSocketEvent.webidl +++ /dev/null @@ -1,16 +0,0 @@ -/* -*- Mode: IDL; 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/. - */ - -[Pref="dom.flyweb.enabled"] -interface FlyWebWebSocketEvent : Event { - [SameObject] readonly attribute Request request; - - [Throws] - WebSocket accept(optional DOMString protocol); - - [Throws] - void respondWith(Promise<Response> r); -}; diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index c353e8be7..4536d7d25 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -132,12 +132,6 @@ partial interface Navigator { Promise<BatteryManager> getBattery(); }; -partial interface Navigator { - [NewObject, Pref="dom.flyweb.enabled"] - Promise<FlyWebPublishedServer> publishServer(DOMString name, - optional FlyWebPublishOptions options); -}; - // http://www.w3.org/TR/vibration/#vibration-interface partial interface Navigator { // We don't support sequences in unions yet @@ -331,11 +325,6 @@ partial interface Navigator { }; partial interface Navigator { - [Throws, Pref="dom.presentation.enabled", SameObject] - readonly attribute Presentation? presentation; -}; - -partial interface Navigator { [NewObject, Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist"] readonly attribute LegacyMozTCPSocket mozTCPSocket; }; diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl index 0bd2677df..e811e1cee 100644 --- a/dom/webidl/Performance.webidl +++ b/dom/webidl/Performance.webidl @@ -55,12 +55,14 @@ partial interface Performance { attribute EventHandler onresourcetimingbufferfull; }; +#ifdef MOZ_DEVTOOLS_SERVER // GC microbenchmarks, pref-guarded, not for general use (bug 1125412) [Exposed=Window] partial interface Performance { [Pref="dom.enable_memory_stats"] readonly attribute object mozMemory; }; +#endif // http://www.w3.org/TR/user-timing/ [Exposed=(Window,Worker)] diff --git a/dom/webidl/Presentation.webidl b/dom/webidl/Presentation.webidl deleted file mode 100644 index d5b331616..000000000 --- a/dom/webidl/Presentation.webidl +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentation - */ - -[Pref="dom.presentation.enabled"] -interface Presentation { - /* - * This should be used by the UA as the default presentation request for the - * controller. When the UA wishes to initiate a PresentationConnection on the - * controller's behalf, it MUST start a presentation connection using the default - * presentation request (as if the controller had called |defaultRequest.start()|). - * - * Only used by controlling browsing context (senders). - */ - [Pref="dom.presentation.controller.enabled"] - attribute PresentationRequest? defaultRequest; - - /* - * This should be available on the receiving browsing context in order to - * access the controlling browsing context and communicate with them. - */ - [SameObject, - Pref="dom.presentation.receiver.enabled"] - readonly attribute PresentationReceiver? receiver; -}; diff --git a/dom/webidl/PresentationAvailability.webidl b/dom/webidl/PresentationAvailability.webidl deleted file mode 100644 index f72b88565..000000000 --- a/dom/webidl/PresentationAvailability.webidl +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationavailability - */ - -[Pref="dom.presentation.controller.enabled"] -interface PresentationAvailability : EventTarget { - /* - * If there is at least one device discovered by UA, the value is |true|. - * Otherwise, its value should be |false|. - */ - readonly attribute boolean value; - - /* - * It is called when device availability changes. - */ - attribute EventHandler onchange; -}; diff --git a/dom/webidl/PresentationConnection.webidl b/dom/webidl/PresentationConnection.webidl deleted file mode 100644 index 9676d2069..000000000 --- a/dom/webidl/PresentationConnection.webidl +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationconnection - */ - -enum PresentationConnectionState -{ - // The initial state when a PresentationConnection is ceated. - "connecting", - - // Existing presentation, and the communication channel is active. - "connected", - - // Existing presentation, but the communication channel is inactive. - "closed", - - // The presentation is nonexistent anymore. It could be terminated manually, - // or either controlling or receiving browsing context is no longer available. - "terminated" -}; - -enum PresentationConnectionBinaryType -{ - "blob", - "arraybuffer" -}; - -[Pref="dom.presentation.enabled"] -interface PresentationConnection : EventTarget { - /* - * Unique id for all existing connections. - */ - [Constant] - readonly attribute DOMString id; - - /* - * Specifies the connection's presentation URL. - */ - readonly attribute DOMString url; - - /* - * @value "connected", "closed", or "terminated". - */ - readonly attribute PresentationConnectionState state; - - attribute EventHandler onconnect; - attribute EventHandler onclose; - attribute EventHandler onterminate; - attribute PresentationConnectionBinaryType binaryType; - - /* - * After a communication channel has been established between the controlling - * and receiving context, this function is called to send message out, and the - * event handler "onmessage" will be invoked at the remote side. - * - * This function only works when the state is "connected". - */ - [Throws] - void send(DOMString data); - - [Throws] - void send(Blob data); - - [Throws] - void send(ArrayBuffer data); - - [Throws] - void send(ArrayBufferView data); - - /* - * It is triggered when receiving messages. - */ - attribute EventHandler onmessage; - - /* - * Both the controlling and receiving browsing context can close the - * connection. Then the connection state should turn into "closed". - * - * This function only works when the state is "connected" or "connecting". - */ - [Throws] - void close(); - - /* - * Both the controlling and receiving browsing context can terminate the - * connection. Then the connection state should turn into "terminated". - * - * This function only works when the state is not "connected". - */ - [Throws] - void terminate(); -}; diff --git a/dom/webidl/PresentationConnectionAvailableEvent.webidl b/dom/webidl/PresentationConnectionAvailableEvent.webidl deleted file mode 100644 index 9efecb7d6..000000000 --- a/dom/webidl/PresentationConnectionAvailableEvent.webidl +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationconnectionavailableevent - */ - -[Constructor(DOMString type, - PresentationConnectionAvailableEventInit eventInitDict), - Pref="dom.presentation.enabled"] -interface PresentationConnectionAvailableEvent : Event -{ - [SameObject] - readonly attribute PresentationConnection connection; -}; - -dictionary PresentationConnectionAvailableEventInit : EventInit -{ - required PresentationConnection connection; -}; diff --git a/dom/webidl/PresentationConnectionCloseEvent.webidl b/dom/webidl/PresentationConnectionCloseEvent.webidl deleted file mode 100644 index da6c25545..000000000 --- a/dom/webidl/PresentationConnectionCloseEvent.webidl +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationconnectioncloseevent - */ - -enum PresentationConnectionClosedReason -{ - // The communication encountered an unrecoverable error. - "error", - - // |PresentationConnection.close()| is called by controlling browsing context - // or the receiving browsing context. - "closed", - - // The connection is closed because the destination browsing context - // that owned the connection navigated or was discarded. - "wentaway" -}; - -[Constructor(DOMString type, - PresentationConnectionCloseEventInit eventInitDict), - Pref="dom.presentation.enabled"] -interface PresentationConnectionCloseEvent : Event -{ - readonly attribute PresentationConnectionClosedReason reason; - - // The message is a human readable description of - // how the communication channel encountered an error. - // It is empty when the closed reason is closed or wentaway. - readonly attribute DOMString message; -}; - -dictionary PresentationConnectionCloseEventInit : EventInit -{ - required PresentationConnectionClosedReason reason; - DOMString message = ""; -}; diff --git a/dom/webidl/PresentationConnectionList.webidl b/dom/webidl/PresentationConnectionList.webidl deleted file mode 100644 index 2c90ce9de..000000000 --- a/dom/webidl/PresentationConnectionList.webidl +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationconnectionlist - */ - -[Pref="dom.presentation.receiver.enabled"] -interface PresentationConnectionList : EventTarget { - /* - * Return the non-terminated set of presentation connections in the - * set of presentation controllers. - * TODO: Use FrozenArray once available. (Bug 1236777) - * readonly attribute FrozenArray<PresentationConnection> connections; - */ - [Frozen, Cached, Pure] - readonly attribute sequence<PresentationConnection> connections; - - /* - * It is called when an incoming connection is connected. - */ - attribute EventHandler onconnectionavailable; -}; diff --git a/dom/webidl/PresentationDeviceInfoManager.webidl b/dom/webidl/PresentationDeviceInfoManager.webidl deleted file mode 100644 index 6ccace324..000000000 --- a/dom/webidl/PresentationDeviceInfoManager.webidl +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: IDL; 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/. - */ - -dictionary PresentationDeviceInfo { - DOMString id; - DOMString name; - DOMString type; -}; - -[NavigatorProperty="mozPresentationDeviceInfo", - JSImplementation="@mozilla.org/presentation-device/deviceInfo;1", - Pref="dom.presentation.enabled", - ChromeOnly] -interface PresentationDeviceInfoManager : EventTarget { - // notify if any device updated. - attribute EventHandler ondevicechange; - - // retrieve all available device infos - Promise<sequence<PresentationDeviceInfo>> getAll(); - - // Force all registered device provider to update device information. - void forceDiscovery(); -}; diff --git a/dom/webidl/PresentationReceiver.webidl b/dom/webidl/PresentationReceiver.webidl deleted file mode 100644 index 4acb37cf3..000000000 --- a/dom/webidl/PresentationReceiver.webidl +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationreceiver - */ - -[Pref="dom.presentation.receiver.enabled"] -interface PresentationReceiver { - /* - * Get a list which contains all connected presentation connections - * in a receiving browsing context. - */ - [SameObject, Throws] - readonly attribute Promise<PresentationConnectionList> connectionList; -}; diff --git a/dom/webidl/PresentationRequest.webidl b/dom/webidl/PresentationRequest.webidl deleted file mode 100644 index c0c5fb8a6..000000000 --- a/dom/webidl/PresentationRequest.webidl +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * https://w3c.github.io/presentation-api/#interface-presentationrequest - */ - -[Constructor(DOMString url), - Constructor(sequence<DOMString> urls), - Pref="dom.presentation.controller.enabled"] -interface PresentationRequest : EventTarget { - /* - * A requesting page use start() to start a new connection, and it will be - * returned with the promise. UA may show a prompt box with a list of - * available devices and ask the user to grant permission, choose a device, or - * cancel the operation. - * - * The promise is resolved when the presenting page is successfully loaded and - * the communication channel is established, i.e., the connection state is - * "connected". - * - * The promise may be rejected duo to one of the following reasons: - * - "OperationError": Unexpected error occurs. - * - "NotFoundError": No available device. - * - "AbortError": User dismiss/cancel the device prompt box. - * - "NetworkError": Failed to establish the control channel or data channel. - * - "TimeoutError": Presenting page takes too long to load. - * - "SecurityError": This operation is insecure. - */ - [Throws] - Promise<PresentationConnection> start(); - - /* - * A requesting page can use reconnect(presentationId) to reopen a - * non-terminated presentation connection. - * - * The promise is resolved when a new presentation connection is created. - * The connection state is "connecting". - * - * The promise may be rejected duo to one of the following reasons: - * - "OperationError": Unexpected error occurs. - * - "NotFoundError": Can not find a presentation connection with the presentationId. - * - "SecurityError": This operation is insecure. - */ - [Throws] - Promise<PresentationConnection> reconnect(DOMString presentationId); - - /* - * UA triggers device discovery mechanism periodically and monitor device - * availability. - * - * The promise may be rejected duo to one of the following reasons: - * - "NotSupportedError": Unable to continuously monitor the availability. - * - "SecurityError": This operation is insecure. - */ - [Throws] - Promise<PresentationAvailability> getAvailability(); - - /* - * It is called when a connection associated with a PresentationRequest is created. - * The event is fired for all connections that are created for the controller. - */ - attribute EventHandler onconnectionavailable; - - /* - * A chrome page, or page which has presentation-device-manage permissiongs, - * uses startWithDevice() to start a new connection with specified device, - * and it will be returned with the promise. UA may show a prompt box with a - * list of available devices and ask the user to grant permission, choose a - * device, or cancel the operation. - * - * The promise is resolved when the presenting page is successfully loaded and - * the communication channel is established, i.e., the connection state is - * "connected". - * - * The promise may be rejected duo to one of the following reasons: - * - "OperationError": Unexpected error occurs. - * - "NotFoundError": No available device. - * - "NetworkError": Failed to establish the control channel or data channel. - * - "TimeoutError": Presenting page takes too long to load. - */ - [ChromeOnly, Throws] - Promise<PresentationConnection> startWithDevice(DOMString deviceId); -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 4e3b8f655..156da302d 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -12,6 +12,7 @@ PREPROCESSED_WEBIDL_FILES = [ 'HTMLMediaElement.webidl', 'Navigator.webidl', 'Node.webidl', + 'Performance.webidl', 'Window.webidl', ] @@ -151,10 +152,6 @@ WEBIDL_FILES = [ 'FileSystemDirectoryReader.webidl', 'FileSystemEntry.webidl', 'FileSystemFileEntry.webidl', - 'FlyWebDiscoveryManager.webidl', - 'FlyWebFetchEvent.webidl', - 'FlyWebPublish.webidl', - 'FlyWebWebSocketEvent.webidl', 'FocusEvent.webidl', 'FontFace.webidl', 'FontFaceSet.webidl', @@ -285,15 +282,7 @@ WEBIDL_FILES = [ 'MediaDeviceInfo.webidl', 'MediaDevices.webidl', 'MediaElementAudioSourceNode.webidl', - 'MediaEncryptedEvent.webidl', 'MediaError.webidl', - 'MediaKeyError.webidl', - 'MediaKeyMessageEvent.webidl', - 'MediaKeys.webidl', - 'MediaKeySession.webidl', - 'MediaKeysRequestStatus.webidl', - 'MediaKeyStatusMap.webidl', - 'MediaKeySystemAccess.webidl', 'MediaList.webidl', 'MediaQueryList.webidl', 'MediaRecorder.webidl', @@ -341,7 +330,6 @@ WEBIDL_FILES = [ 'PaintRequestList.webidl', 'PannerNode.webidl', 'ParentNode.webidl', - 'Performance.webidl', 'PerformanceEntry.webidl', 'PerformanceMark.webidl', 'PerformanceMeasure.webidl', @@ -361,13 +349,6 @@ WEBIDL_FILES = [ 'PopupBoxObject.webidl', 'Position.webidl', 'PositionError.webidl', - 'Presentation.webidl', - 'PresentationAvailability.webidl', - 'PresentationConnection.webidl', - 'PresentationConnectionList.webidl', - 'PresentationDeviceInfoManager.webidl', - 'PresentationReceiver.webidl', - 'PresentationRequest.webidl', 'ProcessingInstruction.webidl', 'ProfileTimelineMarker.webidl', 'Promise.webidl', @@ -565,7 +546,6 @@ WEBIDL_FILES = [ 'WebKitCSSMatrix.webidl', 'WebSocket.webidl', 'WheelEvent.webidl', - 'WidevineCDMManifest.webidl', 'WifiOptions.webidl', 'WindowOrWorkerGlobalScope.webidl', 'WindowRoot.webidl', @@ -592,6 +572,19 @@ WEBIDL_FILES = [ 'XULElement.webidl', ] +if CONFIG['MOZ_EME']: + WEBIDL_FILES += [ + 'MediaEncryptedEvent.webidl', + 'MediaKeyError.webidl', + 'MediaKeyMessageEvent.webidl', + 'MediaKeys.webidl', + 'MediaKeySession.webidl', + 'MediaKeysRequestStatus.webidl', + 'MediaKeyStatusMap.webidl', + 'MediaKeySystemAccess.webidl', + 'WidevineCDMManifest.webidl', + ] + if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']: WEBIDL_FILES += [ 'AudioChannelManager.webidl', @@ -702,8 +695,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [ 'PluginCrashedEvent.webidl', 'PopStateEvent.webidl', 'PopupBlockedEvent.webidl', - 'PresentationConnectionAvailableEvent.webidl', - 'PresentationConnectionCloseEvent.webidl', 'ProgressEvent.webidl', 'RecordErrorEvent.webidl', 'ScrollViewChangeEvent.webidl', |