blob: e291197e23db94cda0c7e2e2318f0840045ba238 [file] [log] [blame]
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_
#define COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_
#include "cobalt/script/mozjs/conversion_helpers.h"
#include "cobalt/script/mozjs/mozjs_exception_state.h"
#include "cobalt/script/mozjs/mozjs_user_object_holder.h"
#include "cobalt/script/mozjs/promise_wrapper.h"
#include "cobalt/script/mozjs/type_traits.h"
#include "cobalt/script/mozjs/weak_heap_object.h"
#include "cobalt/script/promise.h"
#include "third_party/mozjs/js/src/jsapi.h"
namespace cobalt {
namespace script {
namespace mozjs {
// Shared functionality for NativePromise<T>. Does not implement the Resolve
// function, since that needs to be specialized for Promise<T>.
template <typename T>
class NativePromiseBase : public Promise<T> {
public:
// ScriptObject boilerplate.
typedef Promise<T> BaseType;
JSObject* handle() const { return promise_resolver_->get().GetObject(); }
const JS::Value& value() const { return promise_resolver_->get().GetValue(); }
bool WasCollected() const { return promise_resolver_->get().WasCollected(); }
// The Promise JS object (not the resolver).
JSObject* promise() const { return promise_resolver_->GetPromise(); }
void Reject() const OVERRIDE {
JS::RootedObject promise_resolver(context_,
promise_resolver_->get().GetObject());
if (promise_resolver) {
JSAutoRequest auto_request(context_);
JSAutoCompartment auto_compartment(context_, promise_resolver);
promise_resolver_->Reject(JS::UndefinedHandleValue);
}
}
void Reject(SimpleExceptionType exception) const OVERRIDE {
JS::RootedObject promise_resolver(context_,
promise_resolver_->get().GetObject());
if (promise_resolver) {
JSAutoRequest auto_request(context_);
JSAutoCompartment auto_compartment(context_, promise_resolver);
JS::RootedValue error_result(
context_, OBJECT_TO_JSVAL(MozjsExceptionState::CreateErrorObject(
context_, exception)));
promise_resolver_->Reject(error_result);
}
}
void Reject(const scoped_refptr<ScriptException>& result) const OVERRIDE {
JS::RootedObject promise_resolver(context_,
promise_resolver_->get().GetObject());
if (promise_resolver) {
JSAutoRequest auto_request(context_);
JSAutoCompartment auto_compartment(context_, promise_resolver);
JS::RootedValue converted_result(context_);
ToJSValue(context_, result, &converted_result);
promise_resolver_->Reject(converted_result);
}
}
protected:
NativePromiseBase(JSContext* context, JS::HandleObject resolver_object)
: context_(context) {
promise_resolver_.emplace(context, resolver_object);
}
NativePromiseBase(JSContext* context, JS::HandleValue resolver_value)
: context_(context) {
DCHECK(resolver_value.isObject());
JS::RootedObject resolver_object(context, &resolver_value.toObject());
promise_resolver_.emplace(context, resolver_object);
}
JSContext* context_;
base::optional<PromiseWrapper> promise_resolver_;
};
// Implements the Resolve() function for T != void.
template <typename T>
class NativePromise : public NativePromiseBase<T> {
public:
NativePromise(JSContext* context, JS::HandleObject resolver_object)
: NativePromiseBase<T>(context, resolver_object) {}
NativePromise(JSContext* context, JS::HandleValue resolver_value)
: NativePromiseBase<T>(context, resolver_value) {}
void Resolve(const T& value) const OVERRIDE {
JS::RootedObject promise_wrapper(
this->context_, this->promise_resolver_->get().GetObject());
if (promise_wrapper) {
JSAutoRequest auto_request(this->context_);
JSAutoCompartment auto_compartment(this->context_, promise_wrapper);
JS::RootedValue converted_value(this->context_);
ToJSValue(this->context_, value, &converted_value);
this->promise_resolver_->Resolve(converted_value);
}
}
};
// Implements the Resolve() function for T == void.
template <>
class NativePromise<void> : public NativePromiseBase<void> {
public:
NativePromise(JSContext* context, JS::HandleObject resolver_object)
: NativePromiseBase<void>(context, resolver_object) {}
NativePromise(JSContext* context, JS::HandleValue resolver_value)
: NativePromiseBase<void>(context, resolver_value) {}
void Resolve() const OVERRIDE {
JS::RootedObject promise_wrapper(context_,
promise_resolver_->get().GetObject());
if (promise_wrapper) {
JSAutoRequest auto_request(context_);
JSAutoCompartment auto_compartment(context_, promise_wrapper);
promise_resolver_->Resolve(JS::UndefinedHandleValue);
}
}
};
template <typename T>
struct TypeTraits<NativePromise<T> > {
typedef MozjsUserObjectHolder<NativePromise<T> > ConversionType;
typedef const ScriptValue<Promise<T> >* ReturnType;
};
// Promise<T> -> JSValue
// Note that JSValue -> Promise<T> is not yet supported.
template <typename T>
inline void ToJSValue(JSContext* context,
const ScriptValue<Promise<T> >* promise_holder,
JS::MutableHandleValue out_value) {
TRACK_MEMORY_SCOPE("Javascript");
if (!promise_holder) {
out_value.set(JS::NullValue());
return;
}
const MozjsUserObjectHolder<NativePromise<T> >* user_object_holder =
base::polymorphic_downcast<
const MozjsUserObjectHolder<NativePromise<T> >*>(promise_holder);
const NativePromise<T>* native_promise =
base::polymorphic_downcast<const NativePromise<T>*>(
user_object_holder->GetScriptValue());
DCHECK(native_promise);
out_value.setObjectOrNull(native_promise->promise());
}
} // namespace mozjs
} // namespace script
} // namespace cobalt
#endif // COBALT_SCRIPT_MOZJS_NATIVE_PROMISE_H_