| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "modules/navigatorconnect/AcceptConnectionObserver.h" |
| |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "bindings/core/v8/ScriptFunction.h" |
| #include "bindings/core/v8/ScriptPromise.h" |
| #include "bindings/core/v8/ScriptValue.h" |
| #include "bindings/core/v8/SerializedScriptValueFactory.h" |
| #include "core/dom/DOMException.h" |
| #include "core/dom/ExceptionCode.h" |
| #include "modules/navigatorconnect/ServicePort.h" |
| #include "modules/navigatorconnect/ServicePortConnectResponse.h" |
| #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h" |
| |
| namespace blink { |
| |
| // Function that is called when the promise passed to acceptConnection is either |
| // rejected or fulfilled. Calls the corresponding method on |
| // AcceptConnectionObserver. |
| class AcceptConnectionObserver::ThenFunction final : public ScriptFunction { |
| public: |
| enum ResolveType { |
| Fulfilled, |
| Rejected, |
| }; |
| |
| static v8::Local<v8::Function> createFunction(ScriptState* scriptState, AcceptConnectionObserver* observer, ResolveType type) |
| { |
| ThenFunction* self = new ThenFunction(scriptState, observer, type); |
| return self->bindToV8Function(); |
| } |
| |
| DEFINE_INLINE_VIRTUAL_TRACE() |
| { |
| visitor->trace(m_observer); |
| ScriptFunction::trace(visitor); |
| } |
| |
| private: |
| ThenFunction(ScriptState* scriptState, AcceptConnectionObserver* observer, ResolveType type) |
| : ScriptFunction(scriptState) |
| , m_observer(observer) |
| , m_resolveType(type) |
| { |
| } |
| |
| ScriptValue call(ScriptValue value) override |
| { |
| ASSERT(m_observer); |
| ASSERT(m_resolveType == Fulfilled || m_resolveType == Rejected); |
| if (m_resolveType == Rejected) |
| m_observer->responseWasRejected(); |
| else |
| m_observer->responseWasResolved(value); |
| m_observer = nullptr; |
| return value; |
| } |
| |
| Member<AcceptConnectionObserver> m_observer; |
| ResolveType m_resolveType; |
| }; |
| |
| AcceptConnectionObserver* AcceptConnectionObserver::create(ServicePortCollection* collection, PassOwnPtr<WebServicePortConnectEventCallbacks> callbacks, WebServicePortID portID, const KURL& targetURL) |
| { |
| return new AcceptConnectionObserver(collection, callbacks, portID, targetURL); |
| } |
| |
| void AcceptConnectionObserver::contextDestroyed() |
| { |
| ContextLifecycleObserver::contextDestroyed(); |
| m_state = Done; |
| } |
| |
| void AcceptConnectionObserver::didDispatchEvent() |
| { |
| ASSERT(executionContext()); |
| if (m_state != Initial) |
| return; |
| responseWasRejected(); |
| } |
| |
| ScriptPromise AcceptConnectionObserver::respondWith(ScriptState* scriptState, ScriptPromise value, ExceptionState& exceptionState) |
| { |
| if (m_state != Initial) { |
| return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "respondWith was already called.")); |
| } |
| |
| m_state = Pending; |
| m_resolver = ScriptPromiseResolver::create(scriptState); |
| ScriptPromise promise = m_resolver->promise(); |
| value.then( |
| ThenFunction::createFunction(scriptState, this, ThenFunction::Fulfilled), |
| ThenFunction::createFunction(scriptState, this, ThenFunction::Rejected)); |
| return promise; |
| } |
| |
| void AcceptConnectionObserver::responseWasRejected() |
| { |
| ASSERT(executionContext()); |
| if (m_resolver) |
| m_resolver->reject(DOMException::create(AbortError)); |
| m_callbacks->onError(); |
| m_state = Done; |
| } |
| |
| void AcceptConnectionObserver::responseWasResolved(const ScriptValue& value) |
| { |
| ASSERT(executionContext()); |
| |
| ScriptState* scriptState = m_resolver->scriptState(); |
| ExceptionState exceptionState(ExceptionState::UnknownContext, nullptr, nullptr, scriptState->context()->Global(), scriptState->isolate()); |
| ServicePortConnectResponse response = ScriptValue::to<ServicePortConnectResponse>(scriptState->isolate(), value, exceptionState); |
| if (exceptionState.hadException()) { |
| exceptionState.reject(m_resolver.get()); |
| m_resolver = nullptr; |
| responseWasRejected(); |
| return; |
| } |
| if (!response.hasAccept() || !response.accept()) { |
| responseWasRejected(); |
| return; |
| } |
| WebServicePort webPort; |
| webPort.targetUrl = m_targetURL; |
| if (response.hasName()) |
| webPort.name = response.name(); |
| if (response.hasData()) { |
| webPort.data = SerializedScriptValueFactory::instance().create(scriptState->isolate(), response.data(), nullptr, exceptionState)->toWireString(); |
| if (exceptionState.hadException()) { |
| exceptionState.reject(m_resolver.get()); |
| m_resolver = nullptr; |
| responseWasRejected(); |
| return; |
| } |
| } |
| webPort.id = m_portID; |
| ServicePort* port = ServicePort::create(m_collection, webPort); |
| m_collection->addPort(port); |
| m_resolver->resolve(port); |
| m_callbacks->onSuccess(webPort); |
| m_state = Done; |
| } |
| |
| AcceptConnectionObserver::AcceptConnectionObserver(ServicePortCollection* collection, PassOwnPtr<WebServicePortConnectEventCallbacks> callbacks, WebServicePortID portID, const KURL& targetURL) |
| : ContextLifecycleObserver(collection->executionContext()) |
| , m_callbacks(callbacks) |
| , m_collection(collection) |
| , m_portID(portID) |
| , m_targetURL(targetURL) |
| , m_state(Initial) |
| { |
| } |
| |
| DEFINE_TRACE(AcceptConnectionObserver) |
| { |
| visitor->trace(m_collection); |
| visitor->trace(m_resolver); |
| ContextLifecycleObserver::trace(visitor); |
| } |
| |
| } // namespace blink |