/* -*- 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/MediaKeyStatusMap.h" #include "nsPIDOMWindow.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/dom/ToJSValue.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeyStatusMap) NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeyStatusMap) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeyStatusMap) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeyStatusMap) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeyStatusMap) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER tmp->mMap = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeyStatusMap) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MediaKeyStatusMap) NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMap) NS_IMPL_CYCLE_COLLECTION_TRACE_END MediaKeyStatusMap::MediaKeyStatusMap(JSContext* aCx, nsPIDOMWindow* aParent, ErrorResult& aRv) : mParent(aParent) , mUpdateError(NS_OK) { mMap = JS::NewMapObject(aCx); if (NS_WARN_IF(!mMap)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); } mozilla::HoldJSObjects(this); } MediaKeyStatusMap::~MediaKeyStatusMap() { mozilla::DropJSObjects(this); } JSObject* MediaKeyStatusMap::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return MediaKeyStatusMapBinding::Wrap(aCx, this, aGivenProto); } nsPIDOMWindow* MediaKeyStatusMap::GetParentObject() const { return mParent; } MediaKeyStatus MediaKeyStatusMap::Get(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aKey, ErrorResult& aRv) const { if (NS_FAILED(mUpdateError)) { aRv.Throw(mUpdateError); return MediaKeyStatus::Internal_error; } JS::Rooted map(aCx, mMap); JS::Rooted key(aCx); JS::Rooted val(aCx); if (!aKey.ToJSVal(aCx, map, &key) || !JS::MapGet(aCx, map, key, &val)) { aRv.Throw(NS_ERROR_FAILURE); return MediaKeyStatus::Internal_error; } bool ok; int index = FindEnumStringIndex( aCx, val, MediaKeyStatusValues::strings, "MediaKeyStatus", "Invalid MediaKeyStatus value", &ok); return ok ? static_cast(index) : MediaKeyStatus::Internal_error; } bool MediaKeyStatusMap::Has(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aKey, ErrorResult& aRv) const { if (NS_FAILED(mUpdateError)) { aRv.Throw(mUpdateError); return false; } JS::Rooted map(aCx, mMap); JS::Rooted key(aCx); bool result = false; if (!aKey.ToJSVal(aCx, map, &key) || !JS::MapHas(aCx, map, key, &result)) { aRv.Throw(NS_ERROR_FAILURE); } return result; } template static void CallMapMethod(JSContext* aCx, const JS::Heap& aMap, JS::MutableHandle aResult, ErrorResult& aRv, nsresult aUpdateError) { if (NS_FAILED(aUpdateError)) { aRv.Throw(aUpdateError); return; } JS::Rooted map(aCx, aMap); JS::Rooted result(aCx); if (!Method(aCx, map, &result)) { aRv.Throw(NS_ERROR_FAILURE); return; } aResult.set(&result.toObject()); } void MediaKeyStatusMap::Keys(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) const { CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); } void MediaKeyStatusMap::Values(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) const { CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); } void MediaKeyStatusMap::Entries(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) const { CallMapMethod(aCx, mMap, aResult, aRv, mUpdateError); } uint32_t MediaKeyStatusMap::GetSize(JSContext* aCx, ErrorResult& aRv) const { if (NS_FAILED(mUpdateError)) { aRv.Throw(mUpdateError); return 0; } JS::Rooted map(aCx, mMap); return JS::MapSize(aCx, map); } static MediaKeyStatus ToMediaKeyStatus(GMPMediaKeyStatus aStatus) { switch (aStatus) { case kGMPUsable: return MediaKeyStatus::Usable; case kGMPExpired: return MediaKeyStatus::Expired; case kGMPOutputDownscaled: return MediaKeyStatus::Output_downscaled; case kGMPOutputNotAllowed: return MediaKeyStatus::Output_not_allowed; case kGMPInternalError: return MediaKeyStatus::Internal_error; default: return MediaKeyStatus::Internal_error; } } static bool ToJSString(JSContext* aCx, GMPMediaKeyStatus aStatus, JS::MutableHandle aResult) { auto val = uint32_t(ToMediaKeyStatus(aStatus)); MOZ_ASSERT(val < ArrayLength(MediaKeyStatusValues::strings)); JSString* str = JS_NewStringCopyN(aCx, MediaKeyStatusValues::strings[val].value, MediaKeyStatusValues::strings[val].length); if (!str) { return false; } aResult.setString(str); return true; } nsresult MediaKeyStatusMap::UpdateInternal(const nsTArray& keys) { AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mParent))) { return NS_ERROR_FAILURE; } jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); JS::Rooted map(cx, mMap); if (!JS::MapClear(cx, map)) { return NS_ERROR_FAILURE; } for (size_t i = 0; i < keys.Length(); i++) { const auto& ks = keys[i]; JS::Rooted key(cx); JS::Rooted val(cx); if (!ToJSValue(cx, TypedArrayCreator(ks.mId), &key) || !ToJSString(cx, ks.mStatus, &val) || !JS::MapSet(cx, map, key, val)) { return NS_ERROR_OUT_OF_MEMORY; } } return NS_OK; } void MediaKeyStatusMap::Update(const nsTArray& keys) { // Since we can't leave the map in a partial update state, we need // to remember the error and throw it next time the interface methods // are called. mUpdateError = UpdateInternal(keys); } } // namespace dom } // namespace mozilla