Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Bug 1580602 - P2: Implement MediaMetadata API. r=bzbarsky
Browse files Browse the repository at this point in the history
Implement the MediaMetadata interface. The API will be enabled behind a
pref.

Depends on D45456

Differential Revision: https://phabricator.services.mozilla.com/D45457
  • Loading branch information
ChunMinChang committed Oct 10, 2019
1 parent 1ea2222 commit 006925f
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 72 deletions.
2 changes: 1 addition & 1 deletion dom/base/Navigator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ dom::MediaCapabilities* Navigator::MediaCapabilities() {

dom::MediaSession* Navigator::MediaSession() {
if (!mMediaSession) {
mMediaSession = new dom::MediaSession();
mMediaSession = new dom::MediaSession(GetWindow());
}
return mMediaSession;
}
Expand Down
122 changes: 111 additions & 11 deletions dom/media/mediasession/MediaMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,29 @@

#include "mozilla/dom/MediaMetadata.h"
#include "mozilla/dom/MediaSessionBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsNetUtil.h"

namespace mozilla {
namespace dom {

// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaMetadata)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaMetadata, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaMetadata)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaMetadata)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaMetadata)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

nsIGlobalObject* MediaMetadata::GetParentObject() const { return nullptr; }
MediaMetadata::MediaMetadata(nsIGlobalObject* aParent, const nsString& aTitle,
const nsString& aArtist, const nsString& aAlbum)
: mParent(aParent), mTitle(aTitle), mArtist(aArtist), mAlbum(aAlbum) {
MOZ_ASSERT(mParent);
}

nsIGlobalObject* MediaMetadata::GetParentObject() const { return mParent; }

JSObject* MediaMetadata::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
Expand All @@ -29,26 +38,117 @@ JSObject* MediaMetadata::WrapObject(JSContext* aCx,
already_AddRefed<MediaMetadata> MediaMetadata::Constructor(
const GlobalObject& aGlobal, const MediaMetadataInit& aInit,
ErrorResult& aRv) {
return nullptr;
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}

RefPtr<MediaMetadata> mediaMetadata =
new MediaMetadata(global, aInit.mTitle, aInit.mArtist, aInit.mAlbum);
mediaMetadata->SetArtworkInternal(aInit.mArtwork, aRv);
return aRv.Failed() ? nullptr : mediaMetadata.forget();
}

void MediaMetadata::GetTitle(nsString& aRetVal) const {}
void MediaMetadata::GetTitle(nsString& aRetVal) const { aRetVal = mTitle; }

void MediaMetadata::SetTitle(const nsAString& aTitle) {}
void MediaMetadata::SetTitle(const nsAString& aTitle) { mTitle = aTitle; }

void MediaMetadata::GetArtist(nsString& aRetVal) const {}
void MediaMetadata::GetArtist(nsString& aRetVal) const { aRetVal = mArtist; }

void MediaMetadata::SetArtist(const nsAString& aArtist) {}
void MediaMetadata::SetArtist(const nsAString& aArtist) { mArtist = aArtist; }

void MediaMetadata::GetAlbum(nsString& aRetVal) const {}
void MediaMetadata::GetAlbum(nsString& aRetVal) const { aRetVal = mAlbum; }

void MediaMetadata::SetAlbum(const nsAString& aAlbum) {}
void MediaMetadata::SetAlbum(const nsAString& aAlbum) { mAlbum = aAlbum; }

void MediaMetadata::GetArtwork(JSContext* aCx, nsTArray<JSObject*>& aRetVal,
ErrorResult& aRv) const {}
ErrorResult& aRv) const {
// Convert the MediaImages to JS Objects
if (!aRetVal.SetCapacity(mArtwork.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}

for (size_t i = 0; i < mArtwork.Length(); ++i) {
JS::RootedValue value(aCx);
if (!ToJSValue(aCx, mArtwork[i], &value)) {
aRv.NoteJSContextException(aCx);
return;
}

JS::Rooted<JSObject*> object(aCx, &value.toObject());
if (!JS_FreezeObject(aCx, object)) {
aRv.NoteJSContextException(aCx);
return;
}

aRetVal.AppendElement(object);
}
}

void MediaMetadata::SetArtwork(JSContext* aCx,
const Sequence<JSObject*>& aArtwork,
ErrorResult& aRv){};
ErrorResult& aRv) {
// Convert the JS Objects to MediaImages
Sequence<MediaImage> artwork;
if (!artwork.SetCapacity(aArtwork.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}

for (JSObject* object : aArtwork) {
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*object));

MediaImage* image = artwork.AppendElement(fallible);
MOZ_ASSERT(image, "The capacity is preallocated");
if (!image->Init(aCx, value)) {
aRv.NoteJSContextException(aCx);
return;
}
}

SetArtworkInternal(artwork, aRv);
};

static nsIURI* GetEntryBaseURL() {
nsCOMPtr<Document> doc = GetEntryDocument();
return doc ? doc->GetDocBaseURI() : nullptr;
}

// `aURL` is an inout parameter.
static nsresult ResolveURL(nsString& aURL, nsIURI* aBaseURI) {
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL,
/* UTF-8 for charset */ nullptr, aBaseURI);
if (NS_FAILED(rv)) {
return rv;
}

nsAutoCString spec;
rv = uri->GetSpec(spec);
if (NS_FAILED(rv)) {
return rv;
}

CopyUTF8toUTF16(spec, aURL);
return NS_OK;
}

void MediaMetadata::SetArtworkInternal(const Sequence<MediaImage>& aArtwork,
ErrorResult& aRv) {
nsTArray<MediaImage> artwork(aArtwork);

nsCOMPtr<nsIURI> baseURI = GetEntryBaseURL();
for (MediaImage& image : artwork) {
nsresult rv = ResolveURL(image.mSrc, baseURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.ThrowTypeError<MSG_INVALID_URL>(image.mSrc);
return;
}
}
mArtwork = std::move(artwork);
}

} // namespace dom
} // namespace mozilla
16 changes: 15 additions & 1 deletion dom/media/mediasession/MediaMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,22 @@ class MediaMetadata final : public nsISupports, public nsWrapperCache {
ErrorResult& aRv);

private:
MediaMetadata() = default;
MediaMetadata(nsIGlobalObject* aParent, const nsString& aTitle,
const nsString& aArtist, const nsString& aAlbum);

~MediaMetadata() = default;

// Perform `convert artwork algorithm`. Set `mArtwork` to a converted
// `aArtwork` if the conversion works, otherwise throw a type error in `aRv`.
void SetArtworkInternal(const Sequence<MediaImage>& aArtwork,
ErrorResult& aRv);

nsCOMPtr<nsIGlobalObject> mParent;

nsString mTitle;
nsString mArtist;
nsString mAlbum;
nsTArray<MediaImage> mArtwork;
};

} // namespace dom
Expand Down
16 changes: 11 additions & 5 deletions dom/media/mediasession/MediaSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,37 @@
* 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/MediaMetadata.h"
#include "mozilla/dom/MediaSession.h"

namespace mozilla {
namespace dom {

// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaSession)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaSession, mParent, mMediaMetadata)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaSession)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaSession)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaSession)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

nsIGlobalObject* MediaSession::GetParentObject() const { return nullptr; }
MediaSession::MediaSession(nsPIDOMWindowInner* aParent) : mParent(aParent) {
MOZ_ASSERT(mParent);
}

nsPIDOMWindowInner* MediaSession::GetParentObject() const { return mParent; }

JSObject* MediaSession::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return MediaSession_Binding::Wrap(aCx, this, aGivenProto);
}

MediaMetadata* MediaSession::GetMetadata() const { return nullptr; }
MediaMetadata* MediaSession::GetMetadata() const { return mMediaMetadata; }

void MediaSession::SetMetadata(MediaMetadata* aMetadata) {}
void MediaSession::SetMetadata(MediaMetadata* aMetadata) {
mMediaMetadata = aMetadata;
// TODO: Perform update-metadata algorithm.
}

void MediaSession::SetActionHandler(MediaSessionAction aAction,
MediaSessionActionHandler* aHandler) {}
Expand Down
11 changes: 8 additions & 3 deletions dom/media/mediasession/MediaSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/MediaMetadata.h"
#include "mozilla/dom/MediaSessionBinding.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"

class nsIGlobalObject;
class nsPIDOMWindowInner;

namespace mozilla {
namespace dom {
Expand All @@ -26,10 +27,10 @@ class MediaSession final : public nsISupports, public nsWrapperCache {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaSession)

MediaSession() = default;
explicit MediaSession(nsPIDOMWindowInner* aParent);

// WebIDL methods
nsIGlobalObject* GetParentObject() const;
nsPIDOMWindowInner* GetParentObject() const;

JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
Expand All @@ -43,6 +44,10 @@ class MediaSession final : public nsISupports, public nsWrapperCache {

private:
~MediaSession() = default;

nsCOMPtr<nsPIDOMWindowInner> mParent;

RefPtr<MediaMetadata> mMediaMetadata;
};

} // namespace dom
Expand Down
52 changes: 1 addition & 51 deletions testing/web-platform/meta/mediasession/mediametadata.html.ini
Original file line number Diff line number Diff line change
@@ -1,52 +1,2 @@
prefs: [dom.media.mediasession.enabled:true]
[mediametadata.html]
[Test that mediaSession.metadata is properly set]
expected: FAIL

[Test that changes to metadata propagate properly]
expected: FAIL

[Test that resetting metadata to null is reflected]
expected: FAIL

[Test that MediaMetadata is constructed using a dictionary]
expected: FAIL

[Test that MediaMetadata constructor can take no parameter]
expected: FAIL

[Test the different values allowed in MediaMetadata init dictionary]
expected: FAIL

[Test the default values for MediaMetadata with empty init dictionary]
expected: FAIL

[Test the default values for MediaMetadata with no init dictionary]
expected: FAIL

[Test that passing unknown values to the dictionary is a no-op]
expected: FAIL

[Test that MediaMetadata is read/write]
expected: FAIL

[Test that MediaMetadat.artwork can't be modified]
expected: FAIL
[Test that MediaMetadata.artwork will not expose unknown properties]
expected: FAIL
[Test that MediaMetadata.artwork is Frozen]
expected: FAIL
[Test that MediaMetadata.artwork returns parsed urls]
expected: FAIL
[Test that MediaMetadata throws when setting an invalid url]
expected: FAIL
[Test MediaImage default values]
expected: FAIL
[Test that MediaImage.src is required]
expected: FAIL
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<title>MediaImage</title>
<script>
function createArtworkFromURLs(sources) {
let artwork = [];
for (const source of sources) {
artwork.push({
src: source
});
}

let metadata = new MediaMetadata({
artwork: artwork
});
return metadata.artwork;
}

</script>
27 changes: 27 additions & 0 deletions testing/web-platform/tests/mediasession/mediametadata.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
<script src="/resources/testharnessreport.js"></script>
<script>

function load_iframe(src) {
return new Promise(resolve => {
const iframe = document.createElement('iframe');
iframe.onload = () => { resolve(iframe); };
iframe.src = src;
iframe.style.display = 'none';
document.documentElement.appendChild(iframe);
});
}

test(function() {
var metadata = new MediaMetadata({});
navigator.mediaSession.metadata = metadata;
Expand Down Expand Up @@ -189,4 +199,21 @@
});
}, "Test that MediaImage.src is required")

promise_test(async t => {
const URLs = [
'http://example.com',
'../foo',
'./foo/bar',
'/foo/bar',
];
const subframe = await load_iframe('helper/artwork-generator.html');
// createArtworkFromURLs is a function in the subframe.
const artwork = subframe.contentWindow.createArtworkFromURLs(URLs);

assert_equals(artwork.length, URLs.length);
for (let i = 0 ; i < artwork.length ; ++i) {
assert_equals(artwork[i].src, new URL(URLs[i], document.URL).href);
}
}, 'Test that the base URL of MediaImage is the base URL of entry setting object');

</script>

0 comments on commit 006925f

Please sign in to comment.