[mojo-bindings] Use Watcher API for JS bindings

BUG=592183

Review URL: https://codereview.chromium.org/1777673003

Cr-Commit-Position: refs/heads/master@{#380092}


CrOS-Libchrome-Original-Commit: b98777bcf33f467bba92cff7de951f0b3fd13b32
diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc
index 66d84a0..404cb9b 100644
--- a/mojo/edk/js/support.cc
+++ b/mojo/edk/js/support.cc
@@ -26,14 +26,26 @@
                            gin::Handle<HandleWrapper> handle,
                            MojoHandleSignals signals,
                            v8::Handle<v8::Function> callback) {
-  return WaitingCallback::Create(args.isolate(), callback, handle, signals)
-             .get();
+  return WaitingCallback::Create(
+      args.isolate(), callback, handle, signals, true /* one_shot */).get();
 }
 
 void CancelWait(WaitingCallback* waiting_callback) {
   waiting_callback->Cancel();
 }
 
+WaitingCallback* Watch(const gin::Arguments& args,
+                       gin::Handle<HandleWrapper> handle,
+                       MojoHandleSignals signals,
+                       v8::Handle<v8::Function> callback) {
+  return WaitingCallback::Create(
+      args.isolate(), callback, handle, signals, false /* one_shot */).get();
+}
+
+void CancelWatch(WaitingCallback* waiting_callback) {
+  waiting_callback->Cancel();
+}
+
 gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
 
 }  // namespace
@@ -47,8 +59,11 @@
 
   if (templ.IsEmpty()) {
     templ = gin::ObjectTemplateBuilder(isolate)
+                // TODO(rockot): Remove asyncWait and cancelWait.
                 .SetMethod("asyncWait", AsyncWait)
                 .SetMethod("cancelWait", CancelWait)
+                .SetMethod("watch", Watch)
+                .SetMethod("cancelWatch", CancelWatch)
                 .Build();
 
     data->SetObjectTemplate(&g_wrapper_info, templ);
diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc
index af34d87..4ba2c61 100644
--- a/mojo/edk/js/waiting_callback.cc
+++ b/mojo/edk/js/waiting_callback.cc
@@ -28,31 +28,32 @@
     v8::Isolate* isolate,
     v8::Handle<v8::Function> callback,
     gin::Handle<HandleWrapper> handle_wrapper,
-    MojoHandleSignals signals) {
+    MojoHandleSignals signals,
+    bool one_shot) {
   gin::Handle<WaitingCallback> waiting_callback = gin::CreateHandle(
-      isolate, new WaitingCallback(isolate, callback, handle_wrapper));
-
-  waiting_callback->handle_watcher_.Start(
-      handle_wrapper->get(), signals, MOJO_DEADLINE_INDEFINITE,
+      isolate, new WaitingCallback(isolate, callback, one_shot));
+  MojoResult result = waiting_callback->watcher_.Start(
+      handle_wrapper->get(), signals,
       base::Bind(&WaitingCallback::OnHandleReady,
                  base::Unretained(waiting_callback.get())));
+
+  // The signals may already be unsatisfiable.
+  if (result == MOJO_RESULT_FAILED_PRECONDITION)
+    waiting_callback->OnHandleReady(MOJO_RESULT_FAILED_PRECONDITION);
+
   return waiting_callback;
 }
 
 void WaitingCallback::Cancel() {
-  if (!handle_watcher_.is_watching())
-    return;
-
-  RemoveHandleCloseObserver();
-  handle_watcher_.Stop();
+  if (watcher_.IsWatching())
+    watcher_.Cancel();
 }
 
 WaitingCallback::WaitingCallback(v8::Isolate* isolate,
                                  v8::Handle<v8::Function> callback,
-                                 gin::Handle<HandleWrapper> handle_wrapper)
-    : handle_wrapper_(handle_wrapper.get()),
+                                 bool one_shot)
+    : one_shot_(one_shot),
       weak_factory_(this) {
-  handle_wrapper_->AddCloseObserver(this);
   v8::Handle<v8::Context> context = isolate->GetCurrentContext();
   runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
   GetWrapper(isolate)
@@ -64,20 +65,7 @@
   Cancel();
 }
 
-void WaitingCallback::RemoveHandleCloseObserver() {
-  handle_wrapper_->RemoveCloseObserver(this);
-  handle_wrapper_ = nullptr;
-}
-
 void WaitingCallback::OnHandleReady(MojoResult result) {
-  RemoveHandleCloseObserver();
-  CallCallback(result);
-}
-
-void WaitingCallback::CallCallback(MojoResult result) {
-  DCHECK(!handle_watcher_.is_watching());
-  DCHECK(!handle_wrapper_);
-
   if (!runner_)
     return;
 
@@ -94,18 +82,11 @@
 
   v8::Handle<v8::Value> args[] = { gin::ConvertToV8(isolate, result) };
   runner_->Call(callback, runner_->global(), 1, args);
-}
 
-void WaitingCallback::OnWillCloseHandle() {
-  handle_watcher_.Stop();
-
-  // This may be called from GC, so we can't execute Javascript now, call
-  // RemoveHandleCloseObserver explicitly, and CallCallback asynchronously.
-  RemoveHandleCloseObserver();
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&WaitingCallback::CallCallback, weak_factory_.GetWeakPtr(),
-                 MOJO_RESULT_INVALID_ARGUMENT));
+  if (one_shot_ || result == MOJO_RESULT_CANCELLED) {
+    runner_.reset();
+    Cancel();
+  }
 }
 
 }  // namespace js
diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h
index f2554bc..1195a98 100644
--- a/mojo/edk/js/waiting_callback.h
+++ b/mojo/edk/js/waiting_callback.h
@@ -11,25 +11,28 @@
 #include "gin/runner.h"
 #include "gin/wrappable.h"
 #include "mojo/edk/js/handle.h"
-#include "mojo/edk/js/handle_close_observer.h"
-#include "mojo/message_pump/handle_watcher.h"
 #include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/watcher.h"
 
 namespace mojo {
 namespace edk {
 namespace js {
 
-class WaitingCallback : public gin::Wrappable<WaitingCallback>,
-                        public HandleCloseObserver {
+class WaitingCallback : public gin::Wrappable<WaitingCallback> {
  public:
   static gin::WrapperInfo kWrapperInfo;
 
   // Creates a new WaitingCallback.
+  //
+  // If |one_shot| is true, the callback will only ever be called at most once.
+  // If false, the callback may be called any number of times until the
+  // WaitingCallback is explicitly cancelled.
   static gin::Handle<WaitingCallback> Create(
       v8::Isolate* isolate,
       v8::Handle<v8::Function> callback,
       gin::Handle<HandleWrapper> handle_wrapper,
-      MojoHandleSignals signals);
+      MojoHandleSignals signals,
+      bool one_shot);
 
   // Cancels the callback. Does nothing if a callback is not pending. This is
   // implicitly invoked from the destructor but can be explicitly invoked as
@@ -39,24 +42,19 @@
  private:
   WaitingCallback(v8::Isolate* isolate,
                   v8::Handle<v8::Function> callback,
-                  gin::Handle<HandleWrapper> handle_wrapper);
+                  bool one_shot);
   ~WaitingCallback() override;
 
-  // Callback from common::HandleWatcher.
+  // Callback from the Watcher.
   void OnHandleReady(MojoResult result);
 
-  // Invoked by the HandleWrapper if the handle is closed while this wait is
-  // still in progress.
-  void OnWillCloseHandle() override;
-
-  void RemoveHandleCloseObserver();
-  void CallCallback(MojoResult result);
+  // Indicates whether this is a one-shot callback or not. If so, it uses the
+  // deprecated HandleWatcher to wait for signals; otherwise it uses the new
+  // system Watcher API.
+  const bool one_shot_;
 
   base::WeakPtr<gin::Runner> runner_;
-
-  common::HandleWatcher handle_watcher_;
-
-  HandleWrapper* handle_wrapper_;
+  Watcher watcher_;
   base::WeakPtrFactory<WaitingCallback> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WaitingCallback);
diff --git a/mojo/public/js/connector.js b/mojo/public/js/connector.js
index e672b54..674f36b 100644
--- a/mojo/public/js/connector.js
+++ b/mojo/public/js/connector.js
@@ -16,17 +16,20 @@
     this.dropWrites_ = false;
     this.error_ = false;
     this.incomingReceiver_ = null;
-    this.readWaitCookie_ = null;
+    this.readWatcher_ = null;
     this.errorHandler_ = null;
 
-    if (handle)
-      this.waitToReadMore_();
+    if (handle) {
+      this.readWatcher_ = support.watch(handle,
+                                        core.HANDLE_SIGNAL_READABLE,
+                                        this.readMore_.bind(this));
+    }
   }
 
   Connector.prototype.close = function() {
-    if (this.readWaitCookie_) {
-      support.cancelWait(this.readWaitCookie_);
-      this.readWaitCookie_ = null;
+    if (this.readWatcher_) {
+      support.cancelWatch(this.readWatcher_);
+      this.readWatcher_ = null;
     }
     if (this.handle_ != null) {
       core.close(this.handle_);
@@ -79,22 +82,14 @@
     return this.error_;
   };
 
-  Connector.prototype.waitToReadMore_ = function() {
-    this.readWaitCookie_ = support.asyncWait(this.handle_,
-                                             core.HANDLE_SIGNAL_READABLE,
-                                             this.readMore_.bind(this));
-  };
-
   Connector.prototype.readMore_ = function(result) {
     for (;;) {
       var read = core.readMessage(this.handle_,
                                   core.READ_MESSAGE_FLAG_NONE);
       if (this.handle_ == null) // The connector has been closed.
         return;
-      if (read.result == core.RESULT_SHOULD_WAIT) {
-        this.waitToReadMore_();
+      if (read.result == core.RESULT_SHOULD_WAIT)
         return;
-      }
       if (read.result != core.RESULT_OK) {
         this.error_ = true;
         if (this.errorHandler_)
@@ -118,9 +113,6 @@
 
   TestConnector.prototype = Object.create(Connector.prototype);
 
-  TestConnector.prototype.waitToReadMore_ = function() {
-  }
-
   TestConnector.prototype.waitForNextMessage = function() {
     var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE,
                          core.DEADLINE_INDEFINITE);
diff --git a/mojo/public/js/support.js b/mojo/public/js/support.js
index 025da6c..7e27504 100644
--- a/mojo/public/js/support.js
+++ b/mojo/public/js/support.js
@@ -9,22 +9,45 @@
 
 while (1);
 
-/*
+/* @deprecated Please use watch()/cancelWatch() instead of
+ *     asyncWait()/cancelWait().
+ *
  * Waits on the given handle until the state indicated by |signals| is
  * satisfied.
  *
  * @param {MojoHandle} handle The handle to wait on.
  * @param {MojoHandleSignals} signals Specifies the condition to wait for.
  * @param {function (mojoResult)} callback Called with the result the wait is
- * complete. See MojoWait for possible result codes.
+ *     complete. See MojoWait for possible result codes.
  *
  * @return {MojoWaitId} A waitId that can be passed to cancelWait to cancel the
- * wait.
+ *     wait.
  */
 function asyncWait(handle, signals, callback) { [native code] }
 
-/*
+/* @deprecated Please use watch()/cancelWatch() instead of
+ *     asyncWait()/cancelWait().
+ *
  * Cancels the asyncWait operation specified by the given |waitId|.
+ *
  * @param {MojoWaitId} waitId The waitId returned by asyncWait.
  */
 function cancelWait(waitId) { [native code] }
+
+/* Begins watching a handle for |signals| to be satisfied or unsatisfiable.
+ *
+  * @param {MojoHandle} handle The handle to watch.
+  * @param {MojoHandleSignals} signals The signals to watch.
+  * @param {function (mojoResult)} calback Called with a result any time
+  *     the watched signals become satisfied or unsatisfiable.
+  *
+  * @param {MojoWatchId} watchId An opaque identifier that identifies this
+  *     watch.
+  */
+function watch(handle, signals, callback) { [native code] }
+
+/* Cancels a handle watch initiated by watch().
+ *
+ * @param {MojoWatchId} watchId The watch identifier returned by watch().
+ */
+function cancelWatch(watchId) { [native code] }