 Chromium Code Reviews
 Chromium Code Reviews Issue 228783007:
  rAc: make requestAutocomplete() return a promise.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master
    
  
    Issue 228783007:
  rAc: make requestAutocomplete() return a promise.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/blink.git@master| Index: Source/core/html/HTMLFormElement.cpp | 
| diff --git a/Source/core/html/HTMLFormElement.cpp b/Source/core/html/HTMLFormElement.cpp | 
| index cfb51cacf91998afe5f5dc2cbdac2188f85e7699..99561fcc63ff24250a7f4863ae1c7cbd6bb893ec 100644 | 
| --- a/Source/core/html/HTMLFormElement.cpp | 
| +++ b/Source/core/html/HTMLFormElement.cpp | 
| @@ -28,8 +28,11 @@ | 
| #include <limits> | 
| #include "HTMLNames.h" | 
| #include "bindings/v8/Dictionary.h" | 
| +#include "bindings/v8/NewScriptState.h" | 
| #include "bindings/v8/ScriptController.h" | 
| #include "bindings/v8/ScriptEventListener.h" | 
| +#include "bindings/v8/ScriptPromiseResolver.h" | 
| +#include "bindings/v8/ScriptValue.h" | 
| #include "core/dom/Attribute.h" | 
| #include "core/dom/Document.h" | 
| #include "core/dom/ElementTraversal.h" | 
| @@ -85,6 +88,67 @@ HTMLFormElement::~HTMLFormElement() | 
| document().formController().willDeleteForm(this); | 
| } | 
| +class AutocompleteRequest { | 
| +public: | 
| + static PassOwnPtr<AutocompleteRequest> create(HTMLFormElement* form) | 
| + { | 
| + return adoptPtr(new AutocompleteRequest(form)); | 
| + } | 
| + | 
| + ScriptPromise promise() | 
| + { | 
| + return m_resolver->promise(); | 
| + } | 
| + | 
| + void setResult(HTMLFormElement::AutocompleteResult result) | 
| + { | 
| + ASSERT(!m_isResultSet); | 
| + m_result = result; | 
| + m_isResultSet = true; | 
| + } | 
| + | 
| + void dispatchAndFulfill() | 
| + { | 
| + ASSERT(m_isResultSet); | 
| + | 
| + String reason; | 
| + if (m_result == HTMLFormElement::AutocompleteResultErrorDisabled) | 
| + reason = "disabled"; | 
| + else if (m_result == HTMLFormElement::AutocompleteResultErrorCancel) | 
| + reason = "cancel"; | 
| + else if (m_result == HTMLFormElement::AutocompleteResultErrorInvalid) | 
| + reason = "invalid"; | 
| + | 
| + RefPtrWillBeRawPtr<Event> event = reason.isEmpty() ? Event::createBubble(EventTypeNames::autocomplete) : AutocompleteErrorEvent::create(reason); | 
| + event->setTarget(m_form); | 
| + m_form->dispatchEvent(event.release()); | 
| + | 
| + v8::Isolate* isolate = m_scriptState->isolate(); | 
| + NewScriptState::Scope scope(m_scriptState.get()); | 
| 
yhirano
2014/04/17 10:57:48
Can you use ScriptPromiseResolverWithContext inste
 
Dan Beam
2014/04/17 23:02:04
Done. (patched in your CL locally)
 | 
| + if (reason.isEmpty()) { | 
| + m_resolver->resolve(v8::Undefined(isolate)); | 
| + } else { | 
| + v8::Handle<v8::Value> error = v8::Exception::Error(v8String(isolate, "requestAutocomplete: failed.")); | 
| + error.As<v8::Object>()->Set(v8String(isolate, "reason"), v8String(isolate, reason)); | 
| + m_resolver->reject(ScriptValue(error, isolate)); | 
| + } | 
| + } | 
| + | 
| +private: | 
| + AutocompleteRequest(HTMLFormElement* form) | 
| + : m_form(form) | 
| + , m_resolver(ScriptPromiseResolver::create(form->executionContext())) | 
| + , m_scriptState(NewScriptState::current(toIsolate(form->executionContext()))) | 
| + , m_isResultSet(false) | 
| + { } | 
| + | 
| + HTMLFormElement* m_form; | 
| + RefPtr<ScriptPromiseResolver> m_resolver; | 
| + RefPtr<NewScriptState> m_scriptState; | 
| + HTMLFormElement::AutocompleteResult m_result; | 
| + bool m_isResultSet; | 
| +}; | 
| + | 
| bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style) | 
| { | 
| if (!m_wasDemoted) | 
| @@ -416,53 +480,55 @@ void HTMLFormElement::reset() | 
| m_isInResetFunction = false; | 
| } | 
| -void HTMLFormElement::requestAutocomplete(const Dictionary& details) | 
| +ScriptPromise HTMLFormElement::requestAutocomplete(const Dictionary& details) | 
| { | 
| - String errorMessage; | 
| + OwnPtr<AutocompleteRequest> request = AutocompleteRequest::create(this); | 
| + ScriptPromise promise = request->promise(); | 
| + String errorMessage; | 
| if (!document().frame()) | 
| errorMessage = "requestAutocomplete: form is not owned by a displayed document."; | 
| else if (!shouldAutocomplete()) | 
| errorMessage = "requestAutocomplete: form autocomplete attribute is set to off."; | 
| else if (!UserGestureIndicator::processingUserGesture()) | 
| errorMessage = "requestAutocomplete: must be called in response to a user gesture."; | 
| + else if (m_pendingAutocompleteRequest) | 
| + errorMessage = "requestAutocomplete: already in progess."; | 
| if (!errorMessage.isEmpty()) { | 
| document().addConsoleMessage(RenderingMessageSource, LogMessageLevel, errorMessage); | 
| - finishRequestAutocomplete(AutocompleteResultErrorDisabled); | 
| + doFinishRequestAutocomplete(AutocompleteResultErrorDisabled, request.release()); | 
| } else { | 
| + m_pendingAutocompleteRequest = request.release(); | 
| document().frame()->loader().client()->didRequestAutocomplete(this, details); | 
| } | 
| + | 
| + return promise; | 
| } | 
| void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result) | 
| { | 
| - RefPtrWillBeRawPtr<Event> event = nullptr; | 
| - if (result == AutocompleteResultSuccess) | 
| - event = Event::createBubble(EventTypeNames::autocomplete); | 
| - else if (result == AutocompleteResultErrorDisabled) | 
| - event = AutocompleteErrorEvent::create("disabled"); | 
| - else if (result == AutocompleteResultErrorCancel) | 
| - event = AutocompleteErrorEvent::create("cancel"); | 
| - else if (result == AutocompleteResultErrorInvalid) | 
| - event = AutocompleteErrorEvent::create("invalid"); | 
| - else | 
| - ASSERT_NOT_REACHED(); | 
| + doFinishRequestAutocomplete(result, m_pendingAutocompleteRequest.release()); | 
| +} | 
| - event->setTarget(this); | 
| - m_pendingAutocompleteEvents.append(event.release()); | 
| +void HTMLFormElement::doFinishRequestAutocomplete(AutocompleteResult result, PassOwnPtr<AutocompleteRequest> request) | 
| +{ | 
| + request->setResult(result); | 
| + m_finishedAutocompleteRequests.append(request); | 
| - // Dispatch events later as this API is meant to work asynchronously in all situations and implementations. | 
| + // Finish the request later as this API is meant to work asynchronously in all situations and implementations. | 
| if (!m_requestAutocompleteTimer.isActive()) | 
| m_requestAutocompleteTimer.startOneShot(0, FROM_HERE); | 
| } | 
| void HTMLFormElement::requestAutocompleteTimerFired(Timer<HTMLFormElement>*) | 
| { | 
| - WillBeHeapVector<RefPtrWillBeMember<Event> > pendingEvents; | 
| - m_pendingAutocompleteEvents.swap(pendingEvents); | 
| - for (size_t i = 0; i < pendingEvents.size(); ++i) | 
| - dispatchEvent(pendingEvents[i].release()); | 
| + WillBeHeapVector<OwnPtrWillBeMember<AutocompleteRequest> > finishedRequests; | 
| + m_finishedAutocompleteRequests.swap(finishedRequests); | 
| + | 
| + RefPtr<HTMLFormElement> protector(this); | 
| + for (size_t i = 0; i < finishedRequests.size(); ++i) | 
| + finishedRequests[i]->dispatchAndFulfill(); | 
| } | 
| void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value) |