Chromium side of geofencing event dispatching.

BUG=383125

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

Cr-Commit-Position: refs/heads/master@{#301965}
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 4c48f63..a0254e3 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -41,6 +41,7 @@
   "+third_party/WebKit/public/platform/WebCursorInfo.h",
   "+third_party/WebKit/public/platform/WebGamepad.h",
   "+third_party/WebKit/public/platform/WebGamepads.h",
+  "+third_party/WebKit/public/platform/WebGeofencingEventType.h",
   "+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
   "+third_party/WebKit/public/platform/WebIDBDatabaseException.h",
   "+third_party/WebKit/public/platform/WebIDBTypes.h",
diff --git a/content/browser/geofencing/geofencing_manager.cc b/content/browser/geofencing/geofencing_manager.cc
index 1147d7d..6899c73 100644
--- a/content/browser/geofencing/geofencing_manager.cc
+++ b/content/browser/geofencing/geofencing_manager.cc
@@ -9,8 +9,12 @@
 #include "base/callback.h"
 #include "content/browser/geofencing/geofencing_service.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
 #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h"
+#include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
+#include "url/gurl.h"
 
 namespace content {
 
@@ -21,7 +25,6 @@
                const blink::WebCircularGeofencingRegion& region,
                const StatusCallback& callback,
                int64 geofencing_registration_id);
-
   int64 service_worker_registration_id;
   GURL service_worker_origin;
   std::string region_id;
@@ -47,6 +50,7 @@
     const GeofencingManager::StatusCallback& callback,
     int64 geofencing_registration_id)
     : service_worker_registration_id(service_worker_registration_id),
+      service_worker_origin(service_worker_origin),
       region_id(region_id),
       region(region),
       geofencing_registration_id(geofencing_registration_id),
@@ -227,6 +231,16 @@
     ClearRegistration(registration);
 }
 
+void GeofencingManager::RegionEntered(int64 geofencing_registration_id) {
+  DispatchGeofencingEvent(blink::WebGeofencingEventTypeEnter,
+                          geofencing_registration_id);
+}
+
+void GeofencingManager::RegionExited(int64 geofencing_registration_id) {
+  DispatchGeofencingEvent(blink::WebGeofencingEventTypeLeave,
+                          geofencing_registration_id);
+}
+
 GeofencingManager::Registration* GeofencingManager::FindRegistration(
     int64 service_worker_registration_id,
     const std::string& region_id) {
@@ -286,4 +300,72 @@
     registrations_.erase(registrations_iterator);
 }
 
+void GeofencingManager::DispatchGeofencingEvent(
+    blink::WebGeofencingEventType event_type,
+    int64 geofencing_registration_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  Registration* registration = FindRegistrationById(geofencing_registration_id);
+  if (!registration ||
+      registration->service_worker_registration_id ==
+          kInvalidServiceWorkerRegistrationId) {
+    // TODO(mek): Log/track these failures.
+    return;
+  }
+
+  service_worker_context_->context()->storage()->FindRegistrationForId(
+      registration->service_worker_registration_id,
+      registration->service_worker_origin,
+      base::Bind(&GeofencingManager::DeliverGeofencingEvent,
+                 this,
+                 event_type,
+                 geofencing_registration_id));
+}
+
+void GeofencingManager::DeliverGeofencingEvent(
+    blink::WebGeofencingEventType event_type,
+    int64 geofencing_registration_id,
+    ServiceWorkerStatusCode service_worker_status,
+    const scoped_refptr<ServiceWorkerRegistration>&
+        service_worker_registration) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  Registration* registration = FindRegistrationById(geofencing_registration_id);
+  if (!registration) {
+    // Geofence got unregistered in the meantime, no longer need to deliver
+    // event.
+    return;
+  }
+
+  if (service_worker_status != SERVICE_WORKER_OK) {
+    // TODO(mek): SW no longer exists, somehow handle this.
+    return;
+  }
+
+  ServiceWorkerVersion* active_version =
+      service_worker_registration->active_version();
+  if (!active_version) {
+    // TODO(mek): No active version, potentially because one is still being
+    //     installed. Handle this somehow.
+    return;
+  }
+
+  // Hold on to the service worker registration in the callback to keep it alive
+  // until the callback dies. Otherwise the registration could be released when
+  // this method returns - before the event is delivered to the service worker.
+  base::Callback<void(ServiceWorkerStatusCode)> dispatch_event_callback =
+      base::Bind(&GeofencingManager::DeliverGeofencingEventEnd,
+                 this,
+                 service_worker_registration);
+  active_version->DispatchGeofencingEvent(dispatch_event_callback,
+                                          event_type,
+                                          registration->region_id,
+                                          registration->region);
+}
+
+void GeofencingManager::DeliverGeofencingEventEnd(
+    const scoped_refptr<ServiceWorkerRegistration>& service_worker_registration,
+    ServiceWorkerStatusCode service_worker_status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // TODO(mek): log/check result.
+}
+
 }  // namespace content
diff --git a/content/browser/geofencing/geofencing_manager.h b/content/browser/geofencing/geofencing_manager.h
index 7cd7b17..29c52e4 100644
--- a/content/browser/geofencing/geofencing_manager.h
+++ b/content/browser/geofencing/geofencing_manager.h
@@ -14,8 +14,10 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "content/browser/geofencing/geofencing_registration_delegate.h"
+#include "content/browser/service_worker/service_worker_storage.h"
 #include "content/common/content_export.h"
 #include "content/common/geofencing_status.h"
+#include "content/common/service_worker/service_worker_status_code.h"
 
 template <typename T>
 struct DefaultSingletonTraits;
@@ -29,6 +31,7 @@
 
 class GeofencingService;
 class ServiceWorkerContextWrapper;
+class ServiceWorkerRegistration;
 
 // This is the main API to the geofencing subsystem. There is one instance of
 // this class per storage partition.
@@ -38,6 +41,8 @@
 // This class is created on the UI thread, but all its methods should only be
 // called from the IO thread.
 // TODO(mek): Implement some kind of persistence of registrations.
+// TODO(mek): Unregister a geofence when the ServiceWorkerRegistration it
+//    belongs to goes away.
 class CONTENT_EXPORT GeofencingManager
     : NON_EXPORTED_BASE(public GeofencingRegistrationDelegate),
       public base::RefCountedThreadSafe<GeofencingManager> {
@@ -102,6 +107,8 @@
   // GeofencingRegistrationDelegate implementation.
   void RegistrationFinished(int64 geofencing_registration_id,
                             GeofencingStatus status) override;
+  void RegionEntered(int64 geofencing_registration_id) override;
+  void RegionExited(int64 geofencing_registration_id) override;
 
   // Looks up a particular geofence registration. Returns nullptr if no
   // registration with the given IDs exists.
@@ -125,6 +132,29 @@
   // Clears a registration.
   void ClearRegistration(Registration* registration);
 
+  // Starts dispatching a particular geofencing |event_type| for the geofence
+  // registration with the given ID. This first looks up the Service Worker
+  // Registration the geofence is associated with, and then attempts to deliver
+  // the event to that service worker.
+  void DispatchGeofencingEvent(blink::WebGeofencingEventType event_type,
+                               int64 geofencing_registration_id);
+
+  // Delivers an event to the specified service worker for the given geofence.
+  // If the geofence registration id is no longer valid, this method does
+  // nothing. This assumes the |service_worker_registration| is the service
+  // worker the geofence registration is associated with.
+  void DeliverGeofencingEvent(blink::WebGeofencingEventType event_type,
+                              int64 geofencing_registration_id,
+                              ServiceWorkerStatusCode service_worker_status,
+                              const scoped_refptr<ServiceWorkerRegistration>&
+                                  service_worker_registration);
+
+  // Called when delivery of a geofence event to a service worker has finished
+  // (or failed to finish).
+  void DeliverGeofencingEventEnd(const scoped_refptr<ServiceWorkerRegistration>&
+                                     service_worker_registration,
+                                 ServiceWorkerStatusCode service_worker_status);
+
   // Map of all registered regions for a particular service worker registration.
   typedef std::map<std::string, Registration> RegionIdRegistrationMap;
   // Map of service worker registration id to the regions registered by that
diff --git a/content/browser/geofencing/geofencing_registration_delegate.h b/content/browser/geofencing/geofencing_registration_delegate.h
index ba7836c..ead0ec8 100644
--- a/content/browser/geofencing/geofencing_registration_delegate.h
+++ b/content/browser/geofencing/geofencing_registration_delegate.h
@@ -19,6 +19,8 @@
  public:
   virtual void RegistrationFinished(int64 geofencing_registration_id,
                                     GeofencingStatus status) = 0;
+  virtual void RegionEntered(int64 geofencing_registration_id) = 0;
+  virtual void RegionExited(int64 geofencing_registration_id) = 0;
 
  protected:
   virtual ~GeofencingRegistrationDelegate() {}
diff --git a/content/browser/geofencing/geofencing_service_unittest.cc b/content/browser/geofencing/geofencing_service_unittest.cc
index 25dd813..7fbe035 100644
--- a/content/browser/geofencing/geofencing_service_unittest.cc
+++ b/content/browser/geofencing/geofencing_service_unittest.cc
@@ -31,6 +31,8 @@
  public:
   MOCK_METHOD2(RegistrationFinished,
                void(int64 geofencing_registration_id, GeofencingStatus status));
+  MOCK_METHOD1(RegionEntered, void(int64 geofencing_registration_id));
+  MOCK_METHOD1(RegionExited, void(int64 geofencing_registration_id));
 };
 
 class MockGeofencingProvider : public GeofencingProvider {
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 62a6a60..30d9fd5 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -374,6 +374,43 @@
   }
 }
 
+void ServiceWorkerVersion::DispatchGeofencingEvent(
+    const StatusCallback& callback,
+    blink::WebGeofencingEventType event_type,
+    const std::string& region_id,
+    const blink::WebCircularGeofencingRegion& region) {
+  DCHECK_EQ(ACTIVATED, status()) << status();
+
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExperimentalWebPlatformFeatures)) {
+    callback.Run(SERVICE_WORKER_ERROR_ABORT);
+    return;
+  }
+
+  if (running_status() != RUNNING) {
+    // Schedule calling this method after starting the worker.
+    StartWorker(base::Bind(&RunTaskAfterStartWorker,
+                           weak_factory_.GetWeakPtr(),
+                           callback,
+                           base::Bind(&self::DispatchGeofencingEvent,
+                                      weak_factory_.GetWeakPtr(),
+                                      callback,
+                                      event_type,
+                                      region_id,
+                                      region)));
+    return;
+  }
+
+  int request_id = geofencing_callbacks_.Add(new StatusCallback(callback));
+  ServiceWorkerStatusCode status =
+      embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
+          request_id, event_type, region_id, region));
+  if (status != SERVICE_WORKER_OK) {
+    geofencing_callbacks_.Remove(request_id);
+    RunSoon(base::Bind(callback, status));
+  }
+}
+
 void ServiceWorkerVersion::AddControllee(
     ServiceWorkerProviderHost* provider_host) {
   DCHECK(!ContainsKey(controllee_map_, provider_host));
@@ -456,6 +493,9 @@
   RunIDMapCallbacks(&push_callbacks_,
                     &StatusCallback::Run,
                     MakeTuple(SERVICE_WORKER_ERROR_FAILED));
+  RunIDMapCallbacks(&geofencing_callbacks_,
+                    &StatusCallback::Run,
+                    MakeTuple(SERVICE_WORKER_ERROR_FAILED));
 
   FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
 
@@ -507,6 +547,8 @@
                         OnSyncEventFinished)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
                         OnPushEventFinished)
+    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GeofencingEventFinished,
+                        OnGeofencingEventFinished)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument,
                         OnPostMessageToDocument)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -660,6 +702,22 @@
   push_callbacks_.Remove(request_id);
 }
 
+void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) {
+  TRACE_EVENT1("ServiceWorker",
+               "ServiceWorkerVersion::OnGeofencingEventFinished",
+               "Request id",
+               request_id);
+  StatusCallback* callback = geofencing_callbacks_.Lookup(request_id);
+  if (!callback) {
+    NOTREACHED() << "Got unexpected message: " << request_id;
+    return;
+  }
+
+  scoped_refptr<ServiceWorkerVersion> protect(this);
+  callback->Run(SERVICE_WORKER_OK);
+  geofencing_callbacks_.Remove(request_id);
+}
+
 void ServiceWorkerVersion::OnPostMessageToDocument(
     int client_id,
     const base::string16& message,
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 24dd459..0dcf9a4 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -23,10 +23,15 @@
 #include "content/common/content_export.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
 
 class GURL;
 
+namespace blink {
+struct WebCircularGeofencingRegion;
+}
+
 namespace content {
 
 class EmbeddedWorkerRegistry;
@@ -151,7 +156,7 @@
   void SendMessage(const IPC::Message& message, const StatusCallback& callback);
 
   // Sends install event to the associated embedded worker and asynchronously
-  // calls |callback| when it errors out or it gets response from the worker
+  // calls |callback| when it errors out or it gets a response from the worker
   // to notify install completion.
   // |active_version_id| must be a valid positive ID
   // if there's an activated (previous) version running.
@@ -164,7 +169,7 @@
                             const StatusCallback& callback);
 
   // Sends activate event to the associated embedded worker and asynchronously
-  // calls |callback| when it errors out or it gets response from the worker
+  // calls |callback| when it errors out or it gets a response from the worker
   // to notify activation completion.
   //
   // This must be called when the status() is INSTALLED. Calling this changes
@@ -183,20 +188,31 @@
                           const FetchCallback& fetch_callback);
 
   // Sends sync event to the associated embedded worker and asynchronously calls
-  // |callback| when it errors out or it gets response from the worker to notify
-  // completion.
+  // |callback| when it errors out or it gets a response from the worker to
+  // notify completion.
   //
   // This must be called when the status() is ACTIVATED.
   void DispatchSyncEvent(const StatusCallback& callback);
 
   // Sends push event to the associated embedded worker and asynchronously calls
-  // |callback| when it errors out or it gets response from the worker to notify
-  // completion.
+  // |callback| when it errors out or it gets a response from the worker to
+  // notify completion.
   //
   // This must be called when the status() is ACTIVATED.
   void DispatchPushEvent(const StatusCallback& callback,
                          const std::string& data);
 
+  // Sends geofencing event to the associated embedded worker and asynchronously
+  // calls |callback| when it errors out or it gets a response from the worker
+  // to notify completion.
+  //
+  // This must be called when the status() is ACTIVATED.
+  void DispatchGeofencingEvent(
+      const StatusCallback& callback,
+      blink::WebGeofencingEventType event_type,
+      const std::string& region_id,
+      const blink::WebCircularGeofencingRegion& region);
+
   // Adds and removes |provider_host| as a controllee of this ServiceWorker.
   // A potential controllee is a host having the version as its .installing
   // or .waiting version.
@@ -262,6 +278,7 @@
                             const ServiceWorkerResponse& response);
   void OnSyncEventFinished(int request_id);
   void OnPushEventFinished(int request_id);
+  void OnGeofencingEventFinished(int request_id);
   void OnPostMessageToDocument(int client_id,
                                const base::string16& message,
                                const std::vector<int>& sent_message_port_ids);
@@ -286,6 +303,7 @@
   IDMap<FetchCallback, IDMapOwnPointer> fetch_callbacks_;
   IDMap<StatusCallback, IDMapOwnPointer> sync_callbacks_;
   IDMap<StatusCallback, IDMapOwnPointer> push_callbacks_;
+  IDMap<StatusCallback, IDMapOwnPointer> geofencing_callbacks_;
 
   ControlleeMap controllee_map_;
   ControlleeByIDMap controllee_by_id_;
diff --git a/content/common/DEPS b/content/common/DEPS
index 46118ac..ddad5b3 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -16,6 +16,7 @@
   "+third_party/WebKit/public/platform/WebFloatRect.h",
   "+third_party/WebKit/public/platform/WebGamepad.h",
   "+third_party/WebKit/public/platform/WebGamepads.h",
+  "+third_party/WebKit/public/platform/WebGeofencingEventType.h",
   "+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
   "+third_party/WebKit/public/platform/WebHTTPBody.h",
   "+third_party/WebKit/public/platform/WebIDBCursor.h",
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 4e85ec0f..f645c56 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -12,6 +12,8 @@
 #include "content/common/service_worker/service_worker_types.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_param_traits.h"
+#include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h"
+#include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerCacheError.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerError.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
@@ -102,6 +104,9 @@
     blink::WebServiceWorkerCacheError,
     blink::WebServiceWorkerCacheErrorLast)
 
+IPC_ENUM_TRAITS_MAX_VALUE(blink::WebGeofencingEventType,
+                          blink::WebGeofencingEventTypeLast)
+
 //---------------------------------------------------------------------------
 // Messages sent from the child process to the browser.
 
@@ -178,6 +183,8 @@
                     int /* request_id */)
 IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_PushEventFinished,
                     int /* request_id */)
+IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_GeofencingEventFinished,
+                    int /* request_id */)
 
 // Asks the browser to retrieve documents controlled by the sender
 // ServiceWorker.
@@ -348,6 +355,11 @@
 IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_PushEvent,
                      int /* request_id */,
                      std::string /* data */)
+IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_GeofencingEvent,
+                     int /* request_id */,
+                     blink::WebGeofencingEventType /* event_type */,
+                     std::string /* region_id */,
+                     blink::WebCircularGeofencingRegion /* region */)
 IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_MessageToWorker,
                      base::string16 /* message */,
                      std::vector<int> /* sent_message_port_ids */,
diff --git a/content/renderer/service_worker/service_worker_script_context.cc b/content/renderer/service_worker/service_worker_script_context.cc
index f6b1168..9aa65d5 100644
--- a/content/renderer/service_worker/service_worker_script_context.cc
+++ b/content/renderer/service_worker/service_worker_script_context.cc
@@ -77,6 +77,7 @@
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_InstallEvent, OnInstallEvent)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SyncEvent, OnSyncEvent)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_PushEvent, OnPushEvent)
+    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GeofencingEvent, OnGeofencingEvent)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToWorker, OnPostMessage)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetClientDocuments,
                         OnDidGetClientDocuments)
@@ -225,6 +226,19 @@
       GetRoutingID(), request_id));
 }
 
+void ServiceWorkerScriptContext::OnGeofencingEvent(
+    int request_id,
+    blink::WebGeofencingEventType event_type,
+    const std::string& region_id,
+    const blink::WebCircularGeofencingRegion& region) {
+  TRACE_EVENT0("ServiceWorker",
+               "ServiceWorkerScriptContext::OnGeofencingEvent");
+  proxy_->dispatchGeofencingEvent(
+      request_id, event_type, blink::WebString::fromUTF8(region_id), region);
+  Send(new ServiceWorkerHostMsg_GeofencingEventFinished(GetRoutingID(),
+                                                        request_id));
+}
+
 void ServiceWorkerScriptContext::OnPostMessage(
     const base::string16& message,
     const std::vector<int>& sent_message_port_ids,
diff --git a/content/renderer/service_worker/service_worker_script_context.h b/content/renderer/service_worker/service_worker_script_context.h
index 44e80ee..f98250d 100644
--- a/content/renderer/service_worker/service_worker_script_context.h
+++ b/content/renderer/service_worker/service_worker_script_context.h
@@ -17,11 +17,13 @@
 #include "content/child/webmessageportchannel_impl.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/renderer/service_worker/service_worker_cache_storage_dispatcher.h"
+#include "third_party/WebKit/public/platform/WebGeofencingEventType.h"
 #include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerClientsInfo.h"
 #include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h"
 
 namespace blink {
+struct WebCircularGeofencingRegion;
 class WebServiceWorkerContextProxy;
 }
 
@@ -82,6 +84,10 @@
   void OnFetchEvent(int request_id, const ServiceWorkerFetchRequest& request);
   void OnSyncEvent(int request_id);
   void OnPushEvent(int request_id, const std::string& data);
+  void OnGeofencingEvent(int request_id,
+                         blink::WebGeofencingEventType event_type,
+                         const std::string& region_id,
+                         const blink::WebCircularGeofencingRegion& region);
   void OnPostMessage(const base::string16& message,
                      const std::vector<int>& sent_message_port_ids,
                      const std::vector<int>& new_routing_ids);